Best way to test if object class is nil? - objective-c

In objective C, what is a better way of doing this:
if ([response class] == [nil class])
response is either a NSDictionary or NSMutableDictionary
The code is working but I'm getting a "Invalid receiver type 'void *' or "comparison of distinct Objective-C types 'Class' and 'struct NSMutableDictionary *' lacks a cast" warning messages
Thanks

If you're actually looking to test [response class], instead of the value of response itself, you'll want to use
if ([response isKindOfClass:[NSNull class])
If you're looking to check if response itself is nil, I describe a nice way of doing this in this answer to a similar question.

An object's class can't be nil unless you've botched some runtime swizzling. All valid objects must be instances of a valid class. Are you trying to do something like the following?
if (!response) {
// 'response' is nil
} else if ([response isKindOfClass:[NSMutableDictionary class]]) {
// response is an NSMutableDictionary
} else if ([response isKindOfClass:[NSDictionary class]]) {
// response is an NSDictionary
// (or an NSMutableDictionary if you remove the above 'if')
}

Since you mentioned either NSDictionary or NSMutableDictionary and appear to be testing for an instances kind...
isKindOfClass: will identify whether or not the receiver is an instance of the class specified. This includes subclasses.
Note that you cannot use this to determine if a dictionary is mutable or immutable, though, as dictionaries are instances of NSCFDictionary which is a subclass of `NSMutableDictionary.
This is very much on purpose.

Related

What's the best way to protect against 'unrecognized selector' for objects returned from NSDictionary

I have the following bit of code where I'm getting a response from the server and trying to parse out error messages.
I notice that on occasion the message object isn't returning as type NSDictionary and will crash the application. I'm wondering what the best practice is to protect against that? In general I try to avoid doing instanceof checks. Likewise has selector checks. It feels like there should be a better way to do this than explicitly check I'm allowed to be using those methods / getting back type that I expect.
NSDictionary *message = [serverErrorJSON objectForKey:#"message"];
if (message != nil) {
return [message objectForKey:#"form"];
}
if ([serverErrorJSON isKindOfClass:NSDictionary.class]) {
return serverErrorJSON[#"message"][#"form"];
}
return nil;
Its good to use the literal syntax both syntactically and programatically.
You don't have to chain them together either, you might want to use another property of the JSON,
if ([serverErrorJSON isKindOfClass:NSDictionary.class]) {
NSDictionary *message = serverErrorJSON[#"message"];
//...
return message[#"form"];
}
return nil;
Your code:
NSDictionary *message = [serverErrorJSON objectForKey:#"message"];
if (message != nil) {
return [message objectForKey:#"form"];
}
The reason you are getting "unrecognized selector" here is that [serverErrorJSON objectForKey:#"message"] is not returning an NSDictionary as you expect. Since you are calling objectForKey: on that object, the recommended way to handle this is to wrap that call with respondsToSelector: :
id message = nil;
if ([serverErrorJSON respondsToSelector:#selector(objectForKey:)]){
message = [serverErrorJSON objectForKey:#"message"];
if ([message respondsToSelector:#selector(objectForKey:)]){
return [message objectForKey:#"form"];
}
}
This tests for the presence of the selector (method) you are calling, and is safer and more compatible than using isKindOfClass: and the like. It's always better to test for capabilities rather than class names, etc. You don't care if the object is an NSDictionary, you care if it can provide an object for a key using the method signature you are invoking.
The best way to protect against unrecognized selector errors is to check respondsToSelector or isKindOfClass depending on your use case.
Unfortunately your code can get pretty cluttered if you find yourself needing to verify that an object is a dictionary frequently, which can occur in your situation like yours with nested dictionaries in the data structure.
You can clean things up by adding a category:
#interface NSDictionary (Safe)
//Returns objectForKey if dictionary param is valid, else returns nil
+ (id) _safeObjectForKey: (NSString*) key
dict: (NSDictionary*) dictionary;
#end
#implementation NSDictionary (Safe)
+ (id) _safeObjectForKey:(NSString *)key
dict:(NSDictionary *)dictionary {
if ([dictionary isKindOfClass:[NSDictionary class]]) {
return [dictionary objectForKey:key];
}
return nil;
}
#end
and then to use it, given your example:
NSDictionary *message = [NSDictionary _safeObjectForKey: #"message"
dict: serverErrorJSON];
return [NSDictionary _safeObjectForKey:#"form"
dict:message];
You can do something similar for other common unrecognized selector error generators as well, like NSArray's objectAtIndex etc.

-[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance

I got an exception that says:
-[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance
Is it saying I am trying to access an NSNull object with a key?
Any idea what causes this and how to fix it or debug further?
The way to fix it is to not attempt objectForKeyedSubscript on an NSNull object. (I'm betting you're handling some JSON data and aren't prepared for a NULL value.)
(And apparently objectForKeyedSubscript is what the new array[x] notation translates into.)
(Note that you can test for NSNull by simply comparing with == to [NSNull null], since there's one and only one NSNull object in the app.)
What ever value you are storing, despite what the editor tells you, at run time you are storing an NSNull, and later on trying to call objectForKeyedSubscript. I am guessing this happening on what is expected to be an NSDictionary. Some thing like:
NSString *str = dict[#"SomeKey"]
Either a piece of code beforehand is not doing its job and investigate there, or perform some validation:
NSDictionary *dict = ...;
if ( [dict isKindOfClass:[NSDictionary class]] ) {
// handle the dictionary
}
else {
// some kind of error, handle appropriately
}
I often have this kind of scenario when dealing with error messages from networking operations.
I suggest adding a category to NSNull to handle this in the same way you would expect a subscript call to be handled if it it were sent to nil.
#implementation NSNull (Additions)
- (NSObject*)objectForKeyedSubscript:(id<NSCopying>)key {
return nil;
}
- (NSObject*)objectAtIndexedSubscript:(NSUInteger)idx {
return nil;
}
#end
A simple way to test is like this:
id n = [NSNull null];
n[#""];
n[0];
With this category, this test should be handled successfully/softly.

Check whether an object is an NSArray or NSDictionary

As per subject, how can I check whether an object is an NSArray or NSDictionary?
if([obj isKindOfClass:[NSArray class]]){
//Is array
}else if([obj isKindOfClass:[NSDictionary class]]){
//is dictionary
}else{
//is something else
}
Try
[myObject isKindOfClass:[NSArray class]]
and
[myObject isKindOfClass:[NSDictionary class]]
Both of these should return BOOL values. This is basic use of the NSObject method:
-(BOOL)isKindOfClass:(Class)aClass
For a bit more information, see this answer here:
In Objective-C, how do I test the object type?
Consider the case when you're parsing data from a JSON or XML response. Depending on the parsing library you are using, you may not end up with NSArrays or NSDictionaries. Instead you may have __NSCFArray or __NSCFDictionary.
In that case, the best way to check whether you have an array or a dictionary is to check whether it responds to a selector that only an array or dictionary would respond to:
if([unknownObject respondsToSelector:#selector(lastObject)]){
// You can treat unknownObject as an NSArray
}else if([unknownObject respondsToSelector:#selector(allKeys)]){
// You can treat unknown Object as an NSDictionary
}
Just in case anyone comes late to this party looking for a Swift equivalent, here you go. It's a lot more elegant than the Objective-C version, IMHO, because not only does it check the types, but it casts them to the desired type at the same time:
if let arrayVersion = obj as? NSArray {
// arrayVersion is guaranteed to be a non-`nil` NSArray
} else if let dictionaryVersion = obj as? NSDictionary {
// dictionaryVersion is guaranteed to be a non-`nil` NSDictionary
} else {
// it's neither
}

iOS NSDictionary description with members' type

I've a NSDictionary that contain non omogeneus stuff. I need to understand what types of memebers there are into.
With the [dict description] I understand the values into, but not the type.
There is a way to do that?
Thanks
You can ask each object for it's class (type) and then check if it's the type you want, using NSObject's -isKindOfClass: method. Note that if your object is, say, an NSMutableArray this would return true for [myMutableArray isKindOfClass:[NSArray class]]
Example:
id object = [myDict objectForKey:aKey];
if ([object isKindOfClass:[NSString class]])
// It's an NSString!
Source: developer.apple.com

How do I get class information at runtime in Objective-C?

I have NSMutableArray with different objects in it of different classes. Now I want to get the class name, related stuff and also check if the respective object is NSString or not. How should I go about it?
I was trying something like the following. It wasn't working of course.
for(NSString *string in array){
NSLog(#"Name of the class : %#", [NSString stringWithCString:class_getName(Class id)];
If you're on Mac OS X, you can use [object className], it returns an NSString
for(id obj in array) NSLog(#"Name of the class: %#", [obj className]);
To check if it's a NSString, you should use something like this:
for(id obj in array) {
if ([obj isKindofClass:[NSString class]]) {
// do something
}
}
for(id object in array){
NSLog(#"Name of the class: %#", [object className]);
NSLog(#"Object is a string: %d", [object isKindOfClass:[NSString class]]);
}
Take a look at the NSObject class and protocol for other interesting methods.
I have NSMutableArray with different objects in it of different classes. Now I want to get the class name & related stuff & also check if the respective object is NSString or not.
Hold up. Why do have an array of different typed objects in the first place? Could you redo your design to avoid getting into that situation?
As others have said, -isKindOfClass: works. One downside is it generally leads to brittle code. Here your loop needs to know about all the classes that could be in the array. Sometimes this is the best you can do though.
Designs that use -respondsToSelector: tend to be a little more robust. Here your loop would need to know about the behaviors it depends on of classes in the array.