views:

1084

answers:

3

In my core data model, I have an entity with an optional NSNumber attribute.

How do I test to see if the value in that attribute is valid or not?

When I test for nil... doesn't work.

[self numberAttribute] == nil // always returns NO

When I test for a zero value int, that doesn't work.

[[self numberAttribute] intValue] == 0  // always returns no

As a matter of fact, [[self numberAttribute] intValue]] returns something that looks suspiciously like a pointer to a memory address.

Anyone have any idea what I am doing wrong?

EDIT: OK, the bug was in a totally unrelated part of the code. NSNumber and Core Data are functioning exactly as one would expect. That said, I am going to go with the suggested approach of making the attribute non-optional and specifying 0 as the default value.

Just putting this note here for a little while for anyone who was watching this question, then I am deleting it.

A: 

If you are getting a large value back that looks like a pointer, perhaps it is NaN (not a number)?

If so, you could check with:

 isnan([[self numberAttribute] doubleValue])

Or, possibly it is some other constant along the lines of NSNotFound (though probably more Core Data specific).

Kendall Helmstetter Gelner
Tried several different flavors of this technique, didn't work. The value I'm getting is not a constant, and it reports in the debugger (Summary column) as "Invalid." It's quite bizarre, I assumed if I didn't set the attribute at all, and if it was marked as "optional" with a default value of "0" in the .xcdatamodel, this just would not be this difficult to accomplish.
mmc
If you have it marked as optional, the default value would not be set... I think you are basically getting some kind of invalid object back that represents an indeterminate state. I can't find in Core Data how you are supposed to recognize an invalid object though...
Kendall Helmstetter Gelner
I checked with someone else who has a working Core Data project with optional attributes - he thought that an unset value should in fact be nil, just like you were checking for. So I'd go with the theory that you are getting back an NSNumber object as porneL was postulating.
Kendall Helmstetter Gelner
+2  A: 

According to the documentation,

You can specify that an attribute is optional—that is, it is not required to have a value. In general, however, you are discouraged from doing so—especially for numeric values (typically you can get better results using a mandatory attribute with a default value—in the model—of 0). The reason for this is that SQL has special comparison behavior for NULL that is unlike Objective-C's nil. NULL in a database is not the same as 0, and searches for 0 will not match columns with NULL.

false == (NULL == 0)
false == (NULL != 0)

Moreover, NULL in a database is not equivalent to an empty string or empty data blob, either:

false == (NULL == @"")
false == (NULL != @"")

So, try a test with NULL, which is not nil. Better yet, set a default and change the attribute to be non-optional. That's what I've ended up doing in my models, and in practice it works quite well.

Edit: See my comment above. I tried creating a new managed object with an optional attribute not set, and it was nil.

BOOL isNil = ([newManagedObject numberAttribute] == nil);  //equals YES

Another edit: I came back to this later and checked out an object loaded from the cache in the debugger (gdb is your friend!). It was trying to access memory location 0x0, which is NULL.

Don
Yes, but the real question is if you have an attribute declared optional - how do you check to see if it is set or not?
Kendall Helmstetter Gelner
True. I did some more checking and added detail to my answer.
Don
I agree now that's the proper check, the mystery is that he's not getting nil but some odd value...
Kendall Helmstetter Gelner
A: 

If it looks like a pointer, it may be a pointer to NSNumber instance. Make sure you don't have mismatch between core data model and the class (e.g. NSInteger where NSNumber* is expected).

If you have scalar in your class, then you need to note that yourself in setNilValueForKey:.

porneL
This seems most likely to me a well (not that someone with that awesome a hat needs backup in any way).
Kendall Helmstetter Gelner