前几天在iOS app项目中添加了几个第三方库,各有各的用处,由于一些缘由,有些库是不开源的。app
添加后,发现app编译不经过,错误以下:async
从错误描述中都能看出,app在链接过程当中,发现了一些重复的符号,即一样的OC类和方法在不一样的库中都有实现:liblibPDRCore.a和libsimpleconfiglib.a这两个库有冲突!刚好,这两个库都要用,并且都不开源,仿佛一会儿就走进了死胡同,由于没有办法修改这两个库。函数
网上搜了一下,碰到这种问题的人还真很多,也提出了解决方案:用lipo命令分解其中一个类,删除重复的符号,再用ar命令从新打包成库,我选择修改liblibPDRCore.a这个库:工具
1.查看包信息:lipo -info liblibPDRCore.a,提示fat file,那么表明这个包是支持多平台的,例如armv7,armv64等指针
2.取出armv7包:lipo liblibPDRCore.a -thin armv7 -output armv7/liblibPDRCore_armv7.acode
3.解压出object file(.o文件):cd armv7&ar xv liblibPDRCore_armv7.aip
4.根据出错信息删除PDRSerAsyncSocket.o:rm PDRSerAsyncSocket.oget
5.从新打包:ar rcs liblibPDRCore_armv7.a *.o,记得把旧的库删掉cmd
6.重复1-5把arm64什么的一块儿改了io
7.合并为fat库:lipo -create liblibPDRCore_armv7.a liblibPDRCore_arm64.a -output liblibPDRCore.a
好了,如今用新和成的库替换,并编译,发现编译成功了!
且慢!这和主题OC runtime有什么关系?只是用一些工具去掉了重复符号!
是的,确实没有关系,下面才正式进入主题!
编译是成功了,但实际运行起来呢?很是抱歉,crash了,错误以下:
缘由多是:这两个库虽然有一个名字同样的OC类,有些方法也同样,但并非彻底同样,能够认为,这两个库依赖了另外一个开源库,但不是同一个版本,仿佛又走进死胡同鸟!
仔细分析了一些出错信息:liblibPDRCore.a某个地方调用了AsyncSocket类的方法acceptOnAddress:port:error:,但libsimpleconfiglib.a中的AsyncSocket却没有这个方法,形成调用异常。
如今轮到OC runtime上场了,我也只是抱着试一试的态度。前段时间正好看这方面内容,了解到OC能够在运行时动态增长、替换一些类的方法,解决一些实际问题。如今我想到一个思路:用一个空函数,替代AsyncSocket类的方法acceptOnAddress:port:error:,看是否正常运行
1.先获取类类型:Class _asyncSocketClass = NSClassFromString(@"AsyncSocket");
2.给AsyncSocket类添加方法:class_addMethod(_asyncSocketClass, NSSelectorFromString(@"acceptOnAddress:port:error:"), IMP, types);,这个函数中,后面两个参数,一个是替换方法,一个是方法参数类型,替换方法参数好搞,直接定义一个C格式的函数,传函数指针便可;方法参数类型怎么弄?
因而,我想到了从libsimpleconfiglib.a库中找一些蛛丝马迹,虽然它和liblibPDRCore.a使用了不一样版本的AsyncSocket类,但某些方法应该是相似的,获取能够帮我完成函数class_addMethod所需的最后一个参数
3.用lipo和ar命令得到libsimpleconfiglib.a中的object file:"AsyncSocket.o"
4.查看"AsyncSocket.o"的导出符号:nm AsyncSocket.o,果不其然,发现一个相似的,方法名不一样,参数名却同样
5.再看一下liblibPDRCore.a库中的PDRSerAsyncSocket.o的导出符号,确实有"acceptOnAddress:port:error:"这个方法,咱们知道,这个目标文件已经被咱们去掉了
6.从名称上判断,这两个方法参数同样,并且方法acceptOnInterface确实存在,用method_getTypeEncoding来获取方法的类型便可获得class_addMethod的最后一个参数,用有了这个思路,完成了以下代码:
void impfunc(Class cls, SEL _cmd) { if([NSStringFromSelector(_cmd) isEqualtoString:@"acceptOnAddress"]) { //调用cls的acceptOnInterace的方法 } } - (BOOL)application:... { Class _asyncSocketClass = NSClassFromString(@"AsyncSocket"); Method _acceptOnInterface = class_getInstanceMethod(_asyncSocketClass, NSSelectorFromString(@"acceptOnInterface:port:error:")); class_addMethod(_asyncSocketClass, NSSelectorFromString(@"acceptOnAddress:port:error:"), (IMP)impfunc, method_getTypeEncoding(_acceptOnInterface)); }
7.编译并运行起来以后,确实走到了impfunc,程序也不崩溃了,但相关的功能不正常,我想仍是要真正调用一下acceptOnInterface这个方法才行
8.我想到了解析_cmd的参数,取出参数值,而后再调用objc_msgSend发送消息,尝试了不少方法,没有成功;实际上,我仍是走了弯路,各位应该也看出来了,class_addMethod的第三个参数,直接用acceptOnInterface的实现不就好了!method_getImplementation能够帮咱们作到,因而代码变成以下,编译后运行,成功了!
- (BOOL)application:... { Class _asyncSocketClass = NSClassFromString(@"AsyncSocket"); Method _acceptOnInterface = class_getInstanceMethod(_asyncSocketClass, NSSelectorFromString(@"acceptOnInterface:port:error:")); class_addMethod(_asyncSocketClass, NSSelectorFromString(@"acceptOnAddress:port:error:"), method_getImplementation(_acceptOnInterface ), method_getTypeEncoding(_acceptOnInterface)); }
9.没有了,散会