unrecognized selector sent to class

今天遇到一个问题,在使用一个第三方sdk的时候,报错并闪退,但是注销这个sdk的代码就没问题,所以基本确定是sdk的问题,查看报错日志:

2016-09-18 10:39:31.409 sengoku[2620:98676] +[NSData base64DataFromString:]: unrecognized selector sent to class 0x10e3c5110
2016-09-18 10:39:31.424 sengoku[2620:98676] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NSData base64DataFromString:]: unrecognized selector sent to class 0x10e3c5110'

一般这种都是因为没有这个函数,比如有的时候你自己非法调用了一个函数,但是这个函数并不属于这个类中的函数时,会报unrecognized selector,这时候首先要看下你调用这个函数的对象的类型。

比如NSString的对象调用了NSData的函数,这样就会报错,这时候看报错调用函数对象即可,所以这个看到是[NSData base64DataFromString:]这个函数报错,而NSData自己是没有这个函数的,所以确定了是这个函数的错误。

屏幕快照 2016-09-18 10.41.30.png

因为是第三方库,那么运行Demo就可以,这里报错,解决方法就是在项目的Build setting里面的Linking选项的Other Linker Flags加上

-ObjC

这个参数。

45eed87171c24ca8a3d38bfe93495274.png

错误原因

之所以使用该标志,和Objective-C的一个重要特性:类别(category)有关。根据官方的解释,Unix的标准静态库实现和Objective-C的动态特性之间有一些冲突:Objective-C没有为每个函数(或者方法)定义链接符号,它只为每个类创建链接符号。

这样当在一个静态库中使用类别来扩展已有类的时候,链接器不知道如何把类原有的方法和类别中的方法整合起来,就会导致你调用类别中的方法时,出现"selector not recognized",也就是找不到方法定义的错误。

为了解决这个问题,引入了-ObjC标志,它的作用就是将静态库中所有的和对象相关的文件都加载进来。

通俗来讲就是因为这些方法比如这里的base64DataFromString是这个静态库里面自己拓展的,而OC是没有把这个base64DataFromString方法和NSData整合,所以需要手动加上-ObjC这个标识。

为什么不在每个工程都加上呢,是因为加上这个相当于把静态库所有文件对象都加载进去,软件包会变大。

潜在问题

加上-ObjC之后,是可以全部都加载进来,但是也有可能会报duplicate symbol这个错误,这个错误意思是重复了,就是他们在静态库里面的类别名字恰好和你工程自己增加的类别名字重复了或其它第三方库中的类名重名了,分不清是哪个了,所以会报这个错,解决办法只能修改重复的类别名字了,所以以后自己写的时候,最常见的作法就是给类名加个前缀。以避免别人用你的库时,产生 duplicate symbol 的问题。

-all_load、-force_load、-ObjC的区别

-ObjC已经说了,就是为了把静态库的类别加载进来,但是如果类库中只有category没有类的时候这些category还是加载不进来。

而-all_load和-force-load就是为了解决这个问题。-all_load会强制链接器把目标文件都加载进来,即使没有objc代码。-force_load后面必须跟一个只想静态库的路径。

所以-all_load比-ObjC加载的更多,软件包更大,上面说的那个潜在问题也是存在的,而-force_load则是开发者自己指定加载哪个静态库,比较好点,但是需要指定静态库的路径,比较麻烦。

-all_load和-ObjC使用方式一样,就是在项目的other linker flag里面加上就行了

-force_load需要加路径使用

-force_load $(SRCROOT)/framework/Debug/UIEMultiAccess.framework/UIEMultiAccess

如果是多个库,除了加路径,每个都要加上这个关键词,不能写成-force_load libA.a libB.a libC.a

正确写法是

-force_load libA.a 
-force_load libB.a 
-force_load libC.a

其他

在other Linker Flag里面有时还会有其他的,比如-lxml2,-lz,这些是为了链接库,所以接sdk的时候看好需要配置的条件。

预编译头中加__OBJC__是因为在一个OC工程中,可能包含.m、.mm、.c、.cpp四类编译文件,这四类文件均会引用.pch预编译头。在编译.c、.cpp时,因为语法不兼容OC,所以预编译头中不能包含objc代码。因为.pch是2类源文件共用的,所以在pch中,oc头文件要用__OBJC__包含起来。如下:

25134927-dbe966a63feb4dc7b4e5d7840a935068.png

参考文章

Last modification:January 1st, 1970 at 08:00 am
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

Leave a Comment