项目是用CocoaPod构建的,CocoaPod在生成项目的时候会自动在Other Link Flags配置项上打上-ObjC,而且,即使强行去除-ObjC选项也无法解决,虽然编译可通过,但是运行的时候,友盟、MJRefresh等众多库均会报错无法运行。
同时,公司的项目基于另一个分公司所提供的一个基础服务framework,这个framework具体实现未知,目前看来应该是使用c++开发,同时库必须禁用掉-ObjC选项,否则会报 duplicated symbols错误,编译都无法通过。
于是坑就这样出来了。
-ObjC是干嘛的?
简单来说,-ObjC链接指令是用来解决static library在运行时调用category方法报selector not recognized错误时使用的。也就是说,如果你在一个static library里面声明了一个category,在运行的时候调用这个方法就很有可能会出现这个错误,而这个错误本不应该出现,因为你已经定义了那个方法。那么,为什么会出现这样一个问题?
为啥会出 method not recognized 错误
简单来说,这是因为UNIX 的静态库(.a文件)与OC的动态机制之间的不协调导致的。
我们先来看一般情况下,UNIX静态库及C程序的一个链接过程。当一个C语言程序编译的时候,所有的源代码会被编译为对象文件,即.o文件(object file)。这些对象文件中包含了相应的可执行程序,以及相应的静态数据。链接器最终需要将所有这些对象文件组合到一起从而产生一个最终的可执行文件。
当一个源文件引用了定义于其他文件中的一些东西的时候(比如引用其他文件的一个方法),一个 undefined symbol就被写入了它所产生的object 文件,然后等待最终被解释掉。在最终构建可执行文件的时候,链接器将从包含这些undefined symbols的object文件中拉取信息以解决掉之前被标记的undefined symbols。
一个UNIX的静态库其实就是一系统object file的集合,然而,一般情况下只会拉取那些,他需要的object file。而这样做的好处是可以减小最终可执行文件的大小。
举个例子
比如main.c使用一个函数,名叫foo( ),而这个函数定义于B.c里面。在生成.o文件的时候,main.o就会有一个foo( ) 的undefined symbols标记。在链接期间,B.o文件便会被打入最终的可执行文件中。但是,假如另外还有一个C.c,里面定义了的函数并没被使用到,那么最终,这个C.o文件便不会出现在最终的可执行文件中。
Objective-C有什么不同?
然而众所周之,oc是具有一定动态性的语言,只有在最终运行的时候,对象方法的具体实现只有到被调用的时候才会被确定。基于这个原因,Objective-C并没有对方法级别定义符号,而是只对类级别定义符号。
举个栗子。比如在main.c中包含以下代码: [[FooClass alloc] initWithBar:nil]; 那么在链接的时候,main.o在生成的时候就会包含一个未定义符号(Undefined Symbols) FooClass,但却不会定义符号 initWithBar。
所以为什么会产生这个坑?
坑爹的是,Category只是一个方法的集合,而对一个Cateogry中的方法的调用,并不会生成未定义符号,这就意味着链接器并不知道要去加载这个Cateogry文件所生成的object 文件。于是,在最后运行的时候,运行时系统便会无法找到相应方法的定义,从而抛出unrecognized selector 错误。
-ObjC干了啥?
那为啥加上-ObjC就好了? 原来,加上-ObjC选项的时候,链接器便会加载静态Library里面所有Objective-C实现的类和Cateogry。
还有啥选项?
除了-ObjC,文档上还有几个其他的选项,也值得我们关注一下。
-all_load 全加载,意思是加载所有静态库的成员。无论是c还是c++还oc。
-force_load path_to_load 对某个指定静态库全加载。而-all_load则会对所有的库进行全加载。
这到这里,这个问题貌似已经可以解决了。嗯,分公司的开发肯定在framework里面打包了几个没有用到的额外同名实现,而加上-ObjC,会将所有方法全加载上,于是duplicated symbols错误便被抛出。那么,如果libPod,也就是pod库生成的静态库前面加上-force_load,而不加上-ObjC,问题不就顺利解决了?
例如:
编译用TARGET_BUILD_DIR,打包可用BUILT_PRODUCTS_DIR
-force_load $(TARGET_BUILD_DIR)/MJRefresh/libMJRefresh.a
-force_load $(SRCROOT)/Pods/UMengUShare/UShareSDK/SocialLibraries/WeChat/WechatSDK/libWeChatSDK.a
framework类型的需要之后加 /库名
-force_load $(SRCROOT)/Pods/UMengUShare/UShareSDK/SocialLibraries/QQ/QQSDK/TencentOpenAPI.framework/TencentOpenAPI
引用:https://segmentfault.com/a/1190000005859469
参考:http://www.cocoachina.com/ios/20170110/18549.html,