Reputation: 1262
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. ?
Upvotes: 0
Views: 379
Reputation: 5173
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
Upvotes: 2