事情背景
这个坑是前几天踩的,踩的还特别是时候项目马上要发版了,发给测试做最后的验证,测试反馈从log上面看有点诡异,有些手机可以有些手机不可以。因为提测前我是做过自测的,我相信应该没有问题,一同和测试一起继续测试,发现还是有些手机可以有些手机不可以。这么奇怪的问题让我很懵逼呀。按照常理来说应该是要不可以就都不可以呀。因为这是一个请求里面发生的当时我的本能反应是不是网络不稳定呀,但是话又说会来。这如果是网络的问题的话也不会这么巧合呢?好吧 我又回到座位开始吭哧吭哧的查bug了,说好的发完版去健身的,啊啊啊又要泡汤了;因为最近肩膀又开始疼痛了(ps 程序员请保护好自己的身体),今天特意写了一个demo来追踪下原因。(普通的程序员解决问题,优秀的程序员追溯问题的根源,这句话好像哪里有点不太对,开始查找原因吧)。
问题排查
log全开,手机全部部署上看log,刚刚有问题的手机也没有问题呀。我的哥,这什么情况。百思不得其解。然后又用发布demo测试了下,一看好像还真不行。我的天报异常了。
这是什么鬼,我明明写了这个方法呀,为啥会说找不到呢。测试一直问我你是不是给我同样的包呀,为啥之前可以现在不可以。我 我 我开始怀疑人生了。我保证我真的没有改过任何代码除了关闭log。虽然问题找到了但是我还是不知道为啥会这样。想了很久没明白为啥会这样呢。可事实摆在面前呀,就是报异常了。吃完饭回来,突然想到好像有点不一样。之前给测试的包我是直接把sdk的工程直接引入到测试demo的。如下图
我相信很多sdk的开发者测试的时候应该都是这么玩的,因为这样便于调试sdk的问题。可是对外demo里面我引用的是静态.a库。
这是我目前唯一能想到的差别了。一测还真是,只有引入静态库的时候才会出问题,那问题应该是静态库在打包的时候这个方法没有被添加到被扩展的类里面。这个报错的这个方法是很特别的,因为我为了通用所以就使用category特性扩展了NSString。网上一查在静态库中还真有问题,从文章中的原因我们可以看出是:
Unix的标准静态库实现和Objective-C的动态特性之间有一些冲突:Objective-C没有为每个函数(或者方法)定义链接符号,它只为每个类创建链接符号。这样当在一个静态库中使用类别来扩展已有类的时候,链接器不知道如何把类原有的方法和类别中的方法整合起来,就会导致你调用类别中的方法时,出现"selector not recognized",也就是找不到方法定义的错误。为了解决这个问题,引入了-ObjC标志,它的作用就是将静态库中所有的和对象相关的文件都加载进来
这就是问题的根源。但是另外一篇文章中说在64位的操作体统中链接器有一个bug,会导致只包含有类别的静态库无法使用-ObjC标志来加载文件。变通方法是使用-all_load 或者-force_load标志,它们的作用都是加载静态库中所有文件,不过all_load作用于所有的库,而-force_load后面必须要指定具体的文件。
寻找真凶
对于我上面的描述大家可能会认为问题的根源是我的不同引用方式造成的,直到我刚刚写这个demo之前我也一直这么认为的。但是事实真的是这样吗? NO ,事情的真相是我的开发demo里面增加了-ObjC标志,发布demo里面没有。这才是真正的原因。因为在我刚刚写CategoryExtendDemo的时候我发现不管我是直接引入工程还是引入静态库都会抛出异常。所以说明这两种引用方式没问题。 上面的两篇文章说了是没有增加-ObjC标志的原因。果真我在我的测试demo里面增加这个标志之后两种方式都不会出问题。看来这才是真正的原因。果然我在我的开发demo里面也发现了我确实增加了-ObjC标志。
为了进一步测试如果扩展了自定义的类是否也会有同样的问题,我在代码里面测试了一个自定义的类
@interface CustomCategory : NSObject
@end
@implementation CustomCategory
@end
//扩展类
@interface CustomCategory(Extend)
- (void)extendMethod;
@end
@implementation CustomCategory(Extend)
- (void)extendMethod{
NSLog(@"CustomCategory extendMethod");
}
@end
总结
1、在静态库中如果我们使用了category扩展方法,不管是系统的还是自定义的类如果没有添加-Objc相关标志,都会抛出unrecognized selector sent to instance 异常。
2、在测试sdk的时候一定要和发布包操作环境一样,不然真的不知道哪个环节坑了自己,最重要的是还坑了队友。
3、在sdk这种要提供给第三方使用的代码里面减少使用category这种类似黑科技的特性,因为我们不能确保用户会增加-ObjC链接标志,因为如果我们只是在静态库编译的时候加上这个标志,用户没有加上也同样会抛出异常。