user1366911 user1366911 - 5 months ago 41
Swift Question

NSMutableDictionary isKindOfClass NSDictionary is false

Why is the following code snippet ONLY printing blah3 and blah4, but not blah2?

isKindOfClass
is supposed to respect inheritance, but it appears to be failing exactly that.

Obj-C:

NSDictionary *dict = [NSDictionary new];
NSMutableDictionary *mutableDict = [NSMutableDictionary new];

if([dict isKindOfClass:[mutableDict class]]){
NSLog(@"blah"); //does not print (as expected)
}

if([mutableDict isKindOfClass:[dict class]]){
NSLog(@"blah2"); //does not print (unexpected!)
}

if([mutableDict isKindOfClass:[mutableDict class]]){
NSLog(@"blah3"); //prints
}

if([dict isKindOfClass:[dict class]]){
NSLog(@"blah4"); //prints
}


Swift:

var dict = NSDictionary()
var mutableDict = NSMutableDictionary()
if(dict.isKindOfClass(mutableDict.dynamicType)) {print("blah")}
if(mutableDict.isKindOfClass(dict.dynamicType)) {print("blah2")}
if(dict.isKindOfClass(dict.dynamicType)) {print("blah3")}
if(mutableDict.isKindOfClass(mutableDict.dynamicType)) {print("blah4")}


Using Xcode 7.3.1, iOS 9.3.

Answer

The reason is, NSDictionary.new is not guaranteed to return an NSDictionary itself - it can return a subclass of it. And your dict happens to be of a subclass like NSDictionary0. Also, the mutable one is probably of a subclass of NSMutableDictionary, like NSMutableDictionaryM.

So your NSMutableDictionary is indeed a NSDictionary, but not it's not inheriting from that other subclass.

You can easily check with isKindOf:[NSDictionary class] instead. You might want to dump or inspect the classes of your concrete objects and be surprised.

Edit: here's a quick and dirty class diagram: enter image description here

As you can see, one is not "kind of" another, although both are NSDictionary.

This is called a "class cluster" and has some tricky implications. NSString is another famous one where the classes rarely are what newcomers expect.

Comments