4

I encountered some code that might potentially use Nil as the class in an isKindOfClass: check and was curious what would happen in that case. Using Xcode 6.2, with the OS X 10.10 SDK and running on 10.9.5:

for (id obj in @[
                 @"foo",
                 @"This is a long string.",
                 [NSString new],
                 [NSString stringWithUTF8String:"Hello, world!"],
                 [@"This is a constructed " stringByAppendingString:@"string."],
                 @YES,
                 @2015,
                 @[],
                 @[ @42, ],
                 @[ @"hello", ],
                 @{ @"hi": @5, },
                 ]) {
    NSLog(@"[obj className] => %@; [obj isKindOfClass:Nil] => %@", [obj className], [obj isKindOfClass:Nil] ? @"YES" : @"NO");
}

produces:

[obj className] => __NSCFConstantString; [obj isKindOfClass:Nil] => YES
[obj className] => __NSCFConstantString; [obj isKindOfClass:Nil] => YES
[obj className] => __NSCFConstantString; [obj isKindOfClass:Nil] => YES
[obj className] => __NSCFString; [obj isKindOfClass:Nil] => NO
[obj className] => __NSCFString; [obj isKindOfClass:Nil] => NO
[obj className] => __NSCFBoolean; [obj isKindOfClass:Nil] => NO
[obj className] => __NSCFNumber; [obj isKindOfClass:Nil] => NO
[obj className] => __NSArrayI; [obj isKindOfClass:Nil] => NO
[obj className] => __NSArrayI; [obj isKindOfClass:Nil] => NO
[obj className] => __NSArrayI; [obj isKindOfClass:Nil] => NO
[obj className] => __NSDictionaryI; [obj isKindOfClass:Nil] => NO

Why does [obj isKindOfClass:Nil] return YES for __NSCFConstantString objects and nothing else (or, at least, nothing else I've tried)?

edit: Ultimately, the solution in the code I'm working on is to prevent the call from happening when the class is Nil, so this question is really just a curiosity, especially since the behavior is different on iOS 9.1 and OS X 10.11.1 (per the comments).

Isaac
  • 10,668
  • 5
  • 59
  • 68
  • Interesting: yes, usefull: not so much. The first three are all constants created at compile-time, the others are created at run-time. – zaph Oct 23 '15 at 17:01
  • 1
    Good question… Another question: Why would you do `[obj isKindOfClass:Nil]` – vikingosegundo Oct 23 '15 at 17:01
  • 3
    FYI - Under iOS 9.1 the same code gives `NO` for all values. – rmaddy Oct 23 '15 at 17:01
  • All are `No` for OSX as well under OSX 10.11.1, Xcode 7.1, 10.11 SDK. – zaph Oct 23 '15 at 17:04
  • And all are `NO` for OS X using Xcode 7.1 under 10.10.5. Might be an issue under OS X 10.9. – rmaddy Oct 23 '15 at 17:05
  • @vikingosegundo: The code is passing the result of another method call as the parameter for `isKindOfClass:` and that other method call can return `Nil`. Basically, there's a missed error check, which I'm fixing, but curiosity got the better of me. – Isaac Oct 23 '15 at 17:06
  • @vikingosegundo: It is necessary if the code is to work as intended on Xcode 6.2 + OS X, apparently. – Isaac Oct 23 '15 at 17:07
  • Just send the method to a nil object. It is save. it will immediately return nil – vikingosegundo Oct 23 '15 at 17:07
  • 1
    [Here's the source for NSObject](http://www.opensource.apple.com/source/objc4/objc4-532/runtime/NSObject.mm). Dig a little deeper and see why it's an issue under OS X 10.9. – rmaddy Oct 23 '15 at 17:07
  • @vikingosegundo The object on which `isKindOfClass:` is being called isn't `nil`; it's the `Class` parameter that's `Nil`, and seems to result in inconsistent behavior. – Isaac Oct 23 '15 at 17:08
  • I still fail to see why you must secure it. can u post a code example? – vikingosegundo Oct 23 '15 at 17:10
  • @vikingosegundo: I have an `inputObject` and I'm getting the `expectedClass` for `inputObject` from some other source (based on the context, not `inputObject`). If that lookup fails, `expectedClass` is `Nil` but (based on my above test) `[inputObject isKindOfClass:expectedClass]` might return `YES` where `inputObject` can't be of the expected class since we don't know what the expected class should be. – Isaac Oct 23 '15 at 17:14
  • @rmaddy: It looks like `isKindOfClass:` is calling `class_getSuperclass`, defined in http://www.opensource.apple.com/source/objc4/objc4-532/runtime/objc-class.mm, which is calling `_class_getSuperclass`, declared in http://www.opensource.apple.com/source/objc4/objc4-532/runtime/objc-private.h, for which I don't think there's source? – Isaac Oct 23 '15 at 17:41
  • 1
    @Isaac, this indeed sounds like a valid use case, but also like a fragile design. – vikingosegundo Oct 23 '15 at 18:11
  • Many framework methods have undefined results when passed nil/Nil (same thing). You've managed to find one case where the undefined behavior actually changed. Cool! – Avi Oct 25 '15 at 07:45

0 Answers0