0

背景:

假设我正在使用 iOS 6 SDK 开发应用程序。我将部署目标设置为 iOS 5。然后我可以使用 6 中的功能,但为了保持与 5 的兼容性,您必须在代码中进行一些检查:

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

或者

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

到目前为止,一切都很好。据我所知,这些是做事的标准方式。

但是我今天发现,对于新类示例,如果我只是尝试分配/初始化该类中的一个对象而不进行任何检查,它不会在 iOS 5 上崩溃,这是我所期望的,而是返回 null。

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

为什么这不会导致 iOS 5 崩溃?我想这与链接器的工作方式有关,但我预计会崩溃,因为该版本中不存在该符号。

第二个问题是:如果正常测试是使用 NSStringFromClass 方法,这意味着您可以将 +class 方法发送到不存在的类,它将返回 null - 为什么/如何工作?

最后,我注意到我可以创建一个 ViewController,它采用仅在 iOS 6 中定义的协议,并且在 5 上也不会导致任何问题。 ?

4

1 回答 1

2

此行为是由 NS_CLASS_AVAILABLE 宏引起的。这个宏已经在大多数(全部?)UIKit 类上实现,并且对于不可用的类将返回 nil。这允许您使用以下代码检查特定 iOS 版本上是否存在类:

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

现在,您的调用[[UICollectionView alloc] init]是对 nil 类的类方法调用,它将始终返回 nil。

要回答您的第二个问题,检查类是否存在的正确方法是检查类是否为 nil,如上所述。不再需要 NSStringFromClass 了。

所以,关于问题 3。我也对这个问题感到惊讶,但似乎协议对象被直接编译到二进制文件中。当您使用最新的 SDK 进行编译时,代码编译良好,并且当在尚未引入协议的 SDK 版本上运行时,该协议将可用,因为不需要链接到缺少的类。这意味着该Protocol对象将是有效的,并且无论您运行什么 iOS 版本,该类都将正确响应conformsToProtocol:而没有问题。使用编译的二进制文件可以很容易地看到otool -l这一点,它将显示类及其方法所遵循的协议。协议本身似乎存在于一个名为__objc_protolist. 符合 UICollectionViewDelegate 和 DataSource 的类的输出如下所示:

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
于 2013-05-15T10:02:40.557 回答