After parsing the JSON response from a webservice using NSJSONSerialization, I use +isKindOfClass: to make sure the server returned the kind of data I expect. Using this method, I ran into some weird behaviour, which I’ll illustrate using an example.
Consider the following objects:
// Definitions
NSDictionary *son = @{ @"firstname" : @"James", @"lastname" : @"Appleseed" };
NSDictionary *daughter = @{ @"firstname" : @"Susan", @"lastname" : @"Appleseed"};
NSArray *children = @[son, daughter];
NSDictionary *father = @{ @"firstname" : @"John", @"lastname" : @"Appleseed" };
NSDictionary *family = @{@"children" : children, @"father" : father};
NSDictionary *pedigree = @{@"family" : family };
Those objects represent deserialized JSON returned from a server. Now if I want to use the array of children to calculate how much children there are using NSArray’s -count, I need to make sure the children object is an NSArray. If the children object for example happens to be a string, while the app expects an array, it’ll crash because strings don’t implement a count method. Consider the following code sequence which implements the described check:
// First test
id _family = [pedigree objectForKey:@"family"];
if ([_family isKindOfClass:[NSDictionary class]])
{
NSDictionary *_family = (NSDictionary *)_family;
id _children = [_family objectForKey:@"children"];
NSLog(@"Children: %@", _children);
NSLog(@"Children classname: %@", NSStringFromClass(children.class));
if ([_children isKindOfClass:[NSArray class]]) {
NSLog(@"Children is an NSArray");
} else {
NSLog(@"Children is not an NSArray");
}
} else {
NSLog(@"Family is not an NSDictionary");
}
After running this code, the console outputs the following:
Children: (null)
Children classname: __NSArrayI
Children is not an NSArray
The console output appears to be extremely remarkable and even contradictory. How could children not be an NSArray while its classname is __NSArrayI?
After a bit of debugging, I found that there are two ways to solve this problem:
- remove this line
NSDictionary *_family = (NSDictionary *)_family; - use a different name than
_familyfor the casted variable
How could this behaviour be explained?
In the line
you define a new variable
_familyin the current scope, which makes the outer variable_familyinvisible. Objective-C pointers are initialized tonilif you compile with ARC.And the output is not contradictory, because you print
which is the class of
children(without the underscore), which is an array. But_children(with underscore) isnilbecause_familyis nil as explained above.In fact you don’t need a type cast if you expect a dictionary. You could just do