Hi
I am using TouchJSON to deserialize some JSON. I have been using it in the past and on those occasions I dealt with occurrences of NSNull manually. I would think the author had to deal with this as well, so me doing that again would just be overhead. I then found this in the documentation:
Avoiding NSNull values in output.
NSData *theJSONData = /* some JSON data */
CJSONDeserializer *theDeserializer = [CJSONDeserializer deserializer];
theDeserializer.nullObject = NULL;
NSError *theError = nil;
id theObject = [theDeserializer deserialize:theJSONData error:&theError];}
The way I understand it the user of the class can pass a C-style null pointer to the deserializer and when it encounters a NSNull it will insert the values (NULL) passed to it. So later on when I use the values I won't get NSNull, but NULL.
This seems strange, the return value is an NSDictionary which can only contain Objects, shouldn't the value default to 'nil' instead?
If it is NULL can I check the values like this?
if([jsonDataDict objectForKey:someKey] == NULL)
It would seem more logically to be able to do this:
if(![jsonDataDict objectForKey:someKey])
No to mention all the cases where passing nil is allowed but passing NULL causes a crash.
Or can I just pass 'nil' to the deserializer?
Much of this stems from me still struggling with nil, NULL, [NSNULL null], maybe I am failing to see the potential caveats in using nil.
For another JSON library, but with the same issues, I've created the following category on NSDictionary:
#implementation NSDictionary (Utility)
// in case of [NSNull null] values a nil is returned ...
- (id)objectForKeyNotNull:(id)key {
id object = [self objectForKey:key];
if (object == [NSNull null])
return nil;
return object;
}
#end
Whenever I deal with JSON data from said library, I retrieve values like this:
NSString *someString = [jsonDictionary objectForKeyNotNull:#"SomeString"];
This way the code in my projects become a lot cleaner and at the same time I don't have to think about dealing with [NSNull null] values and the like.
nil and NULL are actually both equal to zero, so they are, in practice, interchangeable. But you're right, for consistency, the documentation for TouchJSON should have used theDeserializer.nullObject = nil instead of NULL.
Now, when you do that, your second predicate actually works fine:
if (![jsonDataDict objectForKey:someKey])
because TouchJSON omits the key from the dictionary when you have nullObject set to nil (or NULL). When the key doesn't exist in the dictionary, NSDictionary returns nil, which is zero so your if condition works as you expect.
If you don't specify nullObject as nil, you can instead check for null like so:
if ([jsonDataDict objectForKey:someKey] == [NSNull null])
There are libraries which deal with it. One of them is SwiftyJSON in Swift, another one is NSTEasyJSON in Objective-C.
With this library (NSTEasyJSON) it will be easy to deal with such problems. In your case you can just check values you need:
NSTEasyJSON *JSON = [NSTEasyJSON withData:JSONData];
NSString *someValue = JSON[someKey].string;
This value will be NSString or nil and you should not check it for NSNull, NULL yourself.
Related
I assumed that adding an NSNull instance to the CAKeyframeAnimation's values array would result in a nil value being assigned to the property at the appropriate time. (I'm using kCAAnimationDiscrete to step through a sequence of values.)
NSMutableArray *keyframeValues = [NSMutableArray array];
...
[keyframeValues addObject:[NSNull null]];
But what I get is the actual NSNull reference in the property, which I have to explicitly test for and filter out:
- (void)drawInContext:(CGContextRef)ctx {
if (self.myProp == (id)[NSNull null]) ...
I can deal with this, of course, but it looks a little weird, since the NSNull is not type-compatible with the property. I just expected CAKeyframeAnimation to translate this back to nil and am wondering if there is something I'm doing wrong or if this is a bug (or at least a bit surprising).
I am working with objective c for an iphone app.
I see that [dictionary objectForKey:#"key"] return <null>. Doing a if([dictionary objectForKey:#"key"] == nil || [dictionary objectForKey:#"key"] == null) does not seem to catch this case.
Doing a if([[dictionary objectForKey:#"key"] isEqualToString:#"<null>"]) causes my program to crash.
What is the correct expression to catch <null>?
More Details
An if statement for nil still isn't catching the case... Maybe i'm just too tired to see something, but here's additional info:
Dictionary is populated via a url that contains json data like so:
NSURL *url = [NSURL URLWithString:"http://site.com/"];
dataresult = [NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:dataresult options:kNilOptions error:& error];
doing an NSLog on the dictionary gives this output:
{
key = "<null>";
responseMessage = "The email / registration code combination is incorrect";
}
You have an instance of NSNull. Actually, the instance, since it's a singleton.
Cocoa collections can't contain nil, although they may return nil if you try to access something which isn't present.
However, sometimes it's valuable for a program to store a thing meaning "nothing" in a collection. That's what NSNull is for.
As it happens, JSON can represent null objects. So, when converting JSON to a Cocoa collection, those null objects get translated into the NSNull object.
When Cocoa formats a string with a "%#" specifier, a nil value will get formatted as "(null)" with parentheses. An NSNull value will get formatted as "<null>" with angle brackets.
New answer:
Thanks for adding the detail. It looks like the "dataresult" you are setting is not a JSON object so no wonder you're getting wacky results from putting a raw string into "[NSJSONSerialization JSONObjectWithData:]. You may need to do some basic error checking on your data before you call anything JSON related.
Original answer:
First off, if this were my code, I wouldn't name a "NSDictionary" object "array" (and I see you caught my comment in your edit... hope you get some sleep soon!).
Secondly, what you are looking for is "nil", not a string named "<null>". As Apple's documentation for objectForKey: states, if an object is not found for the key you are asking for, a nil is returned. The Xcode console tries to be helpful in converting nil objects to "<null>" strings in it's output.
Do "if [dictionary objectForKey: #"key"] != nil" and you should be happier.
Just use the following code:
if ([[dictionary valueForKey:#"key"] isKindOfClass:[NSNull Class]]{
//This means that the value inside the dictionary is <null>
}
else{
//the value is not <null>
}
This should do it.
The documentation for NSDictionary says that the method objectForKey returns nil if the NSDictionary does not contain your key; however, isn't nil the same as zero? If so, how do I know if the return value means that the dictionary contains the key mapped to zero or if that key is just non-existant?
NSDictionaries (and, indeed, all Cocoa collection objects) can only contain Objective-C objects, not C primitives like int. Therefore, were you to store 0 in a dictionary, you'd do it like this:
[myDictionary setObject:[NSNumber numberWithInt:0] forKey:#"myKey"];
and therefore, retrieving it would go like this:
NSNumber* resultObj = [myDictionary objectForKey:#"myKey"];
if (resultObj == nil)
{
//Key didn't exist
}
else
{
int result = [resultObj intValue];
//Now do your work.
}
This concept of 'wrapping' a C primitive in an object to store it in a collection, then 'unwrapping' it on the other end is very common in Cocoa. (You might also look into NSValue if you're trying to work with non-number values.)
Edited: Although the direction in memory NULL and nil point to happen to be 0x0 and it has always been and it may always be, you can't use that value for other purposes but to define an undefined address in memory. In the case of NULL it means an undefined pointer. nil means an undefined object pointer. The idea behind these #defines is that there's a rigid standard for programmers and programs to know and communicate undefined addresses in memory, and it could change to 0x42 in a new compiler or standard and NOTHING should happen. So, don't think nil or NULL as 0; yes they are, but that's a mere accident. It's not a feature.
nil is not the same as 0. nil is an object pointer, 0 is a numeric value
I'm having some trouble with Managed Objects... imagine that.
Here is one real doozy, maybe someone can help.
So I have a managed object class called "Format" which is an entity. Anyway, it has a property called "slot" that's an NSNumber. Now the number can have values from zero to four, but if it does not have a value then I want the NSNumber object to be equal to "nil" as opposed to zero. I wasn't having any luck with that since evidently being zero is the same as being "nil." Ugh. (Is there a way to tell if the pointer is simply empty, "nil," as opposed to pointing to a zero value?)
At any rate, I tried a work-around which was to render the variable into text like so:
if(#"myFormat.slot" != #"(null)")
NSLog(#"slot==%#",myFormat.slot);
But the problem is that I got this in my log:
slot==(null)
So, OK... what the heck? If #"myFormat.slot" == #"(null)" then how the heck is that if statement resolving...?!?!
I'm truly baffled now... please someone help me out.
You won't ever get a nil back from an attribute. Instead, you get a [NSNull null] object.
In Objective-C, nil and null are not interchangeable. When you see nil you are almost looking at a dereferenced pointer. nil is intended to convey that no object has been assigned to the symbol. null by contrast is the singleton instance of [NSNull null]. It is used as a placeholder to indicate that some value, represented by an object, has not been set. In other words, a value of nil doesn't make sense in Objective-C.
In Core Data, relationships and attributes are not treated the same even though they both return objects. A relationship is a pointer to an external object and therefore can have a return nil if no object has been set on the other side of the relationship. An attribute is a value only held by an object and therefore is always represented by [NSNull null] if not set.
By default, all attributes with numerical value will return an NSNumber object initialized to zero. If you remove the default you get [NSNull null].
However, since [NSNull null] is a singleton you can use a simple pointer comparison to check for it e.g.
if (myMo.numericalAttribute == [NSNull null])...
However, that is considered bad practice.
This if(#"myFormat.slot" != #"(null)") is always true, because #"myString" creates an autoreleased string. Therefore you are checking the addresses of to different autorelease strings and (surprise) they are different.
Have you tried something like this:
if ([myNumber isEqualTo: [NSNumber numberWithInt: 0]]) self.myNumber = nil;
When comparing the contents of 2 NSString objects, use the isEqual: method or, if you need to perform a Unicode-based comparison of strings, use isEqualToString:.
If you want to set myFormat.slot to nil, do this:
myFormat.slot = nil;
To set the value of the slot attribute to zero:
myFormat.slot = [NSNumber numberWithInt:0];
To compare values wrapped in NSNumbers (e.g. check if your slot is equal to the 0 integer value), do this:
if ([myFormat.slot intValue] == 0) { // primitive type comparison
//...
}
or this:
if ([myFormat.slot isEqual:[NSNumber numberWithInt:0]]) { // object comparison
//...
}
but NOT THIS:
if (myFormat.slot == 0) { // pointer comparison!!
//...
}
To check if slot is empty (nil):
if (myFormat.slot == nil) {
//...
}
Keep in mind: Core Data standard attributes are always mapped to non-primitive values (NSString, NSDate, NSNumber). Always use isEqual:, compare: & friends when comparing objects' values. The == operator performs pointer comparison when you use it with non-primitives.
Actually my question here is: are null and nil equivalent or not?
I have an example but I am confused when they are equal when they are not.
NSNull *nullValue = [NSNull null];
NSArray *arrayWithNull = [NSArray arrayWithObject:nullValue];
NSLog(#"arrayWithNull: %#", arrayWithNull);
id aValue = [arrayWithNull objectAtIndex:0];
if (aValue == nil) {
NSLog(#"equals nil");
} else if (aValue == [NSNull null]) {
NSLog(#"equals NSNull instance");
if ([aValue isEqual:nil]) {
NSLog(#"isEqual:nil");
}
}
Here in the above case it shows that both null and nil are not equal and it displays "equals NSNull instance"
NSString *str=NULL;
id str1=nil;
if(str1 == str)
{
printf("\n IS EQUAL........");
}
else
{
printf("\n NOT EQUAL........");
}
And in the second case it shows both are equal and it displays "IS EQUAL".
Anyone's help will be much appreciated.
Thank you,
Monish.
nil and NULL are essentially the same, nil is something like (NSObject *)0, while NULL is more like (void *)0. But both are pointers with an integer value of zero. You can send messages to nil without raising an error.
NSNull and NULL (or nil, of course) are different things, however. You just use NSNull as a helper to add an empty object to an NSArray or another container class, since you can't add nil to them. So instead, you use [NSNull null] as a replacement, and you have to check if an array element is NSNull, not if it's nil (it will never be equal to nil).
From http://www.iphonedevsdk.com/forum/iphone-sdk-development/34826-nil-vs-null.html
nil and NULL are 100% interchangeable.
From:
NULL is for C-style memory pointers.
nil is for Objective-C objects.
Nil is for Objective-C classes.
Whenever you're writing Objective-C code, use nil
Whenever you're writing C code, use NULL
But ultimately they're all defined as the same thing -- (void *)0, I think -- so in practice it doesn't really matter.
The concept is the same, with the difference that it's valid to send messages (call method) to nil.
NSNull is a real (singleton) class, that can be used for arrays or dictionnaries, who don't accept NULL or nil values.
Biggest difference between them: sending a message to an NSNULL object is probably going to cause a crash, whereas it's cool to send any message to nil. For example, if you use a key path to get an array, like so:
NSArray *departmentNames = [departments valueForKey:#"name"];
Then you will have an NSNULL object for any department whose name is nil. So, this is going to cause a crash:
for (NSString *name in departmentNames)
NSLog(#"%#", [name lowercaseString]);
whenever name is NSNull, because you just sent an unknown selector (lowercaseString) to an NSNull.
Lesson: check for the NSNull object in an array before sending any message to its elements.
for (NSString *name in departmentNames)
if (name != [NSNull null])
NSLog(#"%#", [name lowercaseString]);
No, NSNull and nil are not the same. They both represent a lack of value, and you might want to treat them the same, but they are still not equal.
The NSNull object instance represents a null value, for example when you read data from a database that has null values.
The nil value is a null pointer, i.e. it doesn't point to any object instance.
In your second code you don't have any NSNull instance. An NSString pointer that contains a null pointer is not an NSNull instance, it's still just a null pointer. You are comparing one null pointer to another, and they are of course equal.
Make sure you typecast [NSNull null] to object type that you are comparing
NSArray list;
if(list==(NSArray *)[NSNull null])
// do something
otherwise you will receive a warning message saying "Comparison of distinct pointer types('type *' and 'NSNull *')