Question

Background:

Let's say I'm developing an app using the iOS 6 SDK. I have my deployment target set to iOS 5. I can then use features from 6 but in order to keep compatibility with 5, you have to have some checks in your code:

// method only available from 6, class of someObj existed in 5
if (someObj respondToSelector:@selector(aMethod)) {
    [someObj aMethod];
}

Or

// entire class only available from 6
if (NSStringFromClass([SKStoreProductViewController class]) != nil) {
    SKStoreProductViewController *store = [[SKStoreProductViewController alloc] init];
}

so far so good. These are the standard ways of doing things, as far as I know.

But I found out today that for the new class example, if I just try and alloc/init an object from that class without any checks, it doesn't crash on iOS 5 which I would have expected but rather returns null.

// run this on an iOS 5 device
NSLog(@"%@", [UICollectionView alloc] init]);

Why isn't that causing a crash on iOS 5? I guess this is something to do with the way the linker works, but I would expect a crash since that symbol doesn't exist in that version.

The secondary question is this: if the normal test is to use the NSStringFromClass method, this implies you can send the +class method to a non-existent class and it will return null - why/how does that work?

Finally, I noticed I can create a ViewController that adopts a protocol that was only defined in iOS 6 and again it causes no issues on 5. ?

Was it helpful?

Solution

This behaviour is due to the NS_CLASS_AVAILABLE macro. This macro has been implemented on most (all?) UIKit classes, and will return nil for a class that is not available. This allows you to check for the existence of a class on a specific iOS version with the code:

if ([UICollectionView class]) {
    // class exists, must be iOS6+
}

Now, your call to [[UICollectionView alloc] init] is a class method call on a nil class, which will always return nil.

To answer your secondary question, the correct way of checking for class existence is to check if the class is nil or not, as above. NSStringFromClass is not needed any more.

So, on to question 3. I was also surprised by this one, but it appears that protocol objects are compiled directly into the binary. As you are compiling using the latest SDK, the code compiles fine and the protocol will be available when running on an SDK version that has not introduced the protocol yet, as no linking to the missing class is required. This means the Protocol object will be valid, and the class will correctly respond to conformsToProtocol: with no problems, no matter what iOS version you are running. This can be easily seen using otool -l on a compiled binary, which will show the protocols conformed to by a class and their methods. The protocols themselves seem to live in a section called __objc_protolist. Output for a class conforming to UICollectionViewDelegate and DataSource is shown below:

000050a4 0x5cf4
           isa 0x5d08
    superclass 0x0
         cache 0x0
        vtable 0x0
          data 0x5b30 (struct class_ro_t *)
                    flags 0x80
            instanceStart 156
             instanceSize 156
               ivarLayout 0x0
                     name 0x4a0f TTViewController
              baseMethods 0x5b10 (struct method_list_t *)
           entsize 12
             count 2
              name 0x3eb8 viewDidLoad
             types 0x4955 v8@0:4
               imp 0x2620
              name 0x3ec4 didReceiveMemoryWarning
             types 0x4955 v8@0:4
               imp 0x2670
            baseProtocols 0x5ad8
                      count 2
              list[0] 0x5da0 (struct protocol_t *)
                  isa 0x0
                 name 0x4997 UICollectionViewDelegate
            protocols 0x5304
          instanceMethods 0x0 (struct method_list_t *)
             classMethods 0x0 (struct method_list_t *)
      optionalInstanceMethods 0x5310
         optionalClassMethods 0x0
           instanceProperties 0x0
              list[1] 0x5dcc (struct protocol_t *)
                  isa 0x0
                 name 0x49ce UICollectionViewDataSource
            protocols 0x53d8
          instanceMethods 0x53e4 (struct method_list_t *)
               entsize 12
                 count 2
                  name 0x394e collectionView:numberOfItemsInSection:
                 types 0x455d i16@0:4@8i12
                   imp 0x0
                  name 0x3975 collectionView:cellForItemAtIndexPath:
                 types 0x4589 @16@0:4@8@12
                   imp 0x0
             classMethods 0x0 (struct method_list_t *)
      optionalInstanceMethods 0x5404
         optionalClassMethods 0x0
           instanceProperties 0x0
                    ivars 0x0
           weakIvarLayout 0x0
           baseProperties 0x0
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top