usage of isMemberOfClass; returning false - objective-c

In the code below I thought the second condition would be true, but it is turning out as false. Am I missing something? Please help me understand.
NSArray *array = [[NSArray alloc] init];
NSLog(#"%#", NSStringFromClass([array class]));
if ([array isMemberOfClass:[NSObject class]]) {
NSLog(#"Member NSObject"); //Didn't print;
}
if ([array isMemberOfClass:[NSArray class]]) {
NSLog(#"Member NSArray"); //Didn't print; I don't understand why?
}
if ([array isKindOfClass:[NSObject class]]) {
NSLog(#"Kind of NSObject"); //Printed; Expected
}
if ([array isKindOfClass:[NSArray class]]) {
NSLog(#"Kind of NSArray"); //Printed; Expected
}
Edit
I created sub class of NSArray as MyArray and tested its instance using isMemberOfClass as below
if ([myArray isMemberOfClass:[MyArray class]]) {
NSLog(#"Member MyArray"); //Printed;
}
So, I guess isMemberOfClass not possible on NSArray, probably on some other framework classes as well.
Thanks.

This is the correct behavior. Try inspecting the actual class for that object:
NSArray *array = [[NSArray alloc] init];
NSLog(#"%#", NSStringFromClass([array class]));
The output you get is something like:
2013-02-15 23:42:31.272 Project[91998:c07] __NSArrayI
So the actual class is __NSArrayI (a private subclass of NSArray), not NSArray itself. Typically, isKindOfClass: provides more useful results.

NSArray is a class cluster. When you create an object of NSArray, internally it creates the object from its cluster. It adds simplicity to avoid creation of different type of objects depending upon the requirements.
For such cases you should use the function isKindOfClass. It checks the completer hierarchy to identify the kind of object.

You should be using isKindOfClass. Refer this for the difference.

Related

[NSArray arrayWithObjects: nil, nil]

I have a property self.shareURL that may or may not be nil and I'd like to wrap it in array. Obviously, if it's nil I can't do that, so I'd like to have an empty array in that case. So I can write:
NSArray *items = [self shareURL] ? #[[self shareURL]] : #[];
However, I can construct it in one call to shareURL, like this:
NSArray *items = [NSArray arrayWithObjects:[self shareURL], nil];
This works because arrayWithObjects: will stop anyway once it sees the first nil and the stack is not corrupted because Objective-C ABI doesn't require it to clear the varargs in the stack.
Is it ok to use the second form? Or is the first one more clear?
Both options seem ok, but if you are asking for which one is more clear, I'd go for this one:
NSArray *items = [self shareURL] ? #[[self shareURL]] : #[];
Why? Because you are implementing the behaviour you want to achieve in that line, not as a consequence of something happening on the stack like on the second approach. In other words, you are achieving the behaviour you are specifying in the line.
If I'm a developer and I see that code, with the 1st approach I'll understand the behaviour, without any explanation.
Rather than terseness, I would opt for readability:
NSArray *items = nil;
if ([self shareURL]) {
items = #[[self shareURL]];
} else {
items = #[];
}
Both ways are OK, and the first one you can write like this:
NSArray *items = [self shareURL] ? : #[];
This is right.
NSArray *items = [self shareURL] ? #[[self shareURL]] : #[];
Ok, for the record, I actually went with
NSMutableArray *items = [NSMutableArray new];
if (self.shareURL) {
[items addObject: self.shareURL];
}
as it makes more clear that there is a "default" state of empty array, and we try to add one object.

What its __NSDictionaryM. Why NSStringFromClass don't return NSMutableDictionary?

I'm getting something weird that I can't get. I'm using a thirdy-party control (https://github.com/brunow/TableKit.m) and it do:
+ (NSSet *)cellMappingsForObject:(id)object mappings:(NSDictionary *)mappings {
NSString *objectStringName = NSStringFromClass([object class]);
return [mappings objectForKey:objectStringName];
}
It's create a mapping based in the name of the class:
[TKCellMapping mappingForObjectClass:[NSMutableDictionary class] block:^(TKCellMapping *cellMapping) {
//CODE
}];
And it check the datasource objects against it mappings. The objects are build for me as:
while ([rs next]) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (NSString *fieldName in [props allKeys]) {
//CODE
[dict setValue:fieldValue forKey:fieldName];
}
[list addObject :dict]; //<--This is the datasource. The map is against each dict
}
So, everywhere I'm telling it is NSMutableDictionary. But then this weird thing happend.
When it calls NSStringFromClass([NSMutableDictionary class]) it say NSMutableDictionary but when it calls NSStringFromClass([rowDict class]) it say __NSDictionaryM!
Why happend this? I check for the header of __NSDictionaryM and Xcode navigate to NSMutableDictionary (?). I can't create objects from __NSDictionaryM.
BTW, what I need to do for replace NSStringFromClass to get exactly NSMutableDictionary?
It's a private subclass of NSMutableDictionary. It is an NSMutableDictionary. Read more about class clusters here.
i've done it with a subclass check:
if( [[o class] isSubclassOfClass:[NSMutableDictionary class]] ) {
// o is at least a subclass of NSMutableDictionary
}

Fast Enumeration on NSArray of Different Types

I have this question here (as well other quesrtions on SO), and the Apple docs about Objective-C collections and fast enumeration. What is not made clear is if an NSArray populated with different types, and a loop is created like:
for ( NSString *string in myArray )
NSLog( #"%#\n", string );
What exactly happens here? Will the loop skip over anything that is not an NSString? For example, if (for the sake of argument) a UIView is in the array, what would happen when the loop encounters that item?
Why would you want to do that? I think that would cause buggy and unintended behavior. If your array is populated with different elements, use this instead:
for (id object in myArray) {
// Check what kind of class it is
if ([object isKindOfClass:[UIView class]]) {
// Do something
}
else {
// Handle accordingly
}
}
What you are doing in your example is effectively the same as,
for (id object in myArray) {
NSString *string = (NSString *)object;
NSLog(#"%#\n", string);
}
Just because you cast object as (NSString *) doesn't mean string will actually be pointing to an NSString object. Calling NSLog() in this way will call the - (NSString *)description method according to the NSObject protocol, which the class being referenced inside the array may or may not conform to. If it conforms, it will print that. Otherwise, it will crash.
You have to understand that a pointer in obj-c has no type information. Even if you write NSString*, it's only a compilation check. During runtime, everything is just an id.
Obj-c runtime never checks whether objects are of the given class. You can put NSNumbers into NSString pointers without problems. An error appears only when you try to call a method (send a message) which is not defined on the object.
How does fast enumeration work? It's exactly the same as:
for (NSUInteger i = 0; i < myArray.count; i++) {
NSString* string = [myArray objectAtIndex:i];
[...]
}
It's just faster because it operates on lower level.
I just tried a quick example... Here is my code.
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:1];
NSNumber *number = [NSNumber numberWithInteger:6];
[array addObject:number];
[array addObject:#"Second"];
Now if I simply log the object, no problem. The NSNumber instance is being cast as an NSString, but both methods respond to -description, so its not a problem.
for (NSString *string in array)
{
NSLog(#"%#", string);
}
However, if I attempt to log -length on NSString...
for (NSString *string in array)
{
NSLog(#"%i", string.length);
}
... it throws an NSInvalidArgumentException because NSNumber doesn't respond to the -length selector. Long story short, Objective-C gives you a lot of rope. Don't hang yourself with it.
Interesting question. The most generic syntax for fast enumeration is
for ( NSObject *obj in myArray )
NSLog( #"%#\n", obj );
I believe that by doing
for ( NSString *string in myArray )
NSLog( #"%#\n", string );
instead, you are simply casting each object as an NSString. That is, I believe the above is equivalent to
for ( NSObject *obj in myArray ) {
NSString *string = obj;
NSLog( #"%#\n", string );
}
I could not find precise mention of this in Apple's documentation for Fast Enumeration, but you can check it on an example and see what happens.
Since all NSObject's respond to isKindOfClass, you could still keep the casting to a minimum:
for(NSString *string in myArray) {
if (![string isKindOfClass:[NSString class]])
continue;
// proceed, knowing you have a valid NSString *
// ...
}

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
}

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.