一个项目中使用了一个包含 category 的静态库,但是此项目在运行过程中,该静态库调用 category 增加的方法处,却报 selector not recognized 异常。
最佳方案:方案四,赶时间的可以直接查看方案四;
将 category 文件跟静态库一起导入到工程中。
笨,而且多余,在多个地方中存在同一份文件,可能会带来不一致。
不使用 category,将 category 中新增加的方法增加一个参数,此参数就是 category 扩展的类的实例,例如要扩展 NSDictionary,要增加一个(NSString *)JSONString
方法,那么将此方法修改成(NSString *)JSONStringWithDict: (NSDictionary *)dict
,也可以实现想要的效果。
如果是自己写的 category ,修改起来还比较简单 ,但是如果是开源项目中包含的 category,改动的工作量会很大;
需要额外的类,而且会导致使用 category 的好处尽失。
上面的两个方案是在搞不清楚那个错误产生的原因时使用的两个简单、直接的方法,但是都太麻烦了。苹果官方文档中的这个 Q&A QA1490:Building Objective-C static libraries with categories 已经说明了这个问题产生的原因:
这个异常是因为标准 UNIX 静态库、linker 以及 Objective-C 的动态性三者之间的实现导致的,Objective-C 不会为方法定义 linker symbols,它只会为每一个类定义 linker symbols。如果你使用 category 扩展了一个已经存在的类,那么 linker 不会将已有类的实现跟 category 的实现连接起来,这就导致了调用静态库中 category 中新增加的方法时抛出 selector not recognized 的异常。
在使用静态库的 target 要将 -ObjC 选项传递给 linker,这个标志将会使得 linker 将静态库中原始类及 category 的类文件都载入!
在 Xcode 中,查看使用了静态库的那个 target 的 Building Settings,然后找到 Linking 类别中的 Other Linker Flags 选项,设置其值为 -ObjC ;
不过,设置 -ObjC 选项对于 iOS 程序来说有时是不够的,这是因为 linker 中存在一个 bug,所以还是可能会在 -ObjC 的情况下导致 selector not recognized 的异常,为了避免这个 bug,在 Other Linker Flags 中,我们将其值设置为 -all_load 或者 -force_load 即可,见下图:
facebook 的 three20 框架也遇到了这个问题,他们给出了一个更好的解决方案:
1 2 3 4 5 6 7 8 |
|
上面的宏定义在 TTCorePreprocessorMacros.h 文件中,在每个 category 的实现文件开头加上:TT_FIX_CATEGORY_BUG({cateory名字}) ,这样就能避免在 iOS 中使用 -ObjC 的 linker 的 bug,但是记住,还是需要把使用静态库的 Target 中的 Building Setting 的 Other Linker Flags 设置成 -ObjC 。