Storing optional NSNumber in Core Data
Asked Answered
S

3

8

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.

Sorority answered 27/7, 2009 at 14:18 Comment(5)
I don't have enough rep to edit yet, but your second code example has unbalanced brackets (2 opening brackets and 3 closing).Teleost
numberAttribute is specified as a float in the data model? How is the property for numberAttribute declared? What do they accessor implementations look like (if you aren't using @dynamic accessors.) Without this information, your question is impossible to answer accurately.Begat
numberAttribute is an int16. All code is the Core Data default generated code. I think tessaractor is on to how to fix my code below, but the question still stands... if an attribute is optional, how can you test for its existence?Sorority
You still haven't provided the exact code that you are using. An Int16 attribute will be presented at runtime by an NSNumber. If your accessor deals in terms of NSNumber * (which the default accessors do) then testing for nil will tell you whether or not the attribute has been set.Begat
I created a test with a an optional Int 32 attribute. I created a new mananged object, did not set the attribute and checked the value: BOOL isNil = ([newManagedObject numberAttribute] == nil); This came back YES. Are you creating a new object or loading one from the model? If you are loading one, maybe the memory is uninitialized and contains garbage?Mimeograph
M
5

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.

Mimeograph answered 27/7, 2009 at 15:41 Comment(3)
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?Wriest
True. I did some more checking and added detail to my answer.Mimeograph
I agree now that's the proper check, the mystery is that he's not getting nil but some odd value...Wriest
W
0

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).

Wriest answered 27/7, 2009 at 15:28 Comment(3)
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.Sorority
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...Wriest
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.Wriest
R
0

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:.

Reckoner answered 27/7, 2009 at 15:56 Comment(1)
This seems most likely to me a well (not that someone with that awesome a hat needs backup in any way).Wriest

© 2022 - 2024 — McMap. All rights reserved.