使用OC runtime解决第三方库冲突

    前几天在iOS app项目中添加了几个第三方库,各有各的用处,因为一些原因,有些库是不开源的。

    添加后,发现app编译不通过,错误如下:

135042_9tpI_2241986.png

    从错误描述中都能看出,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.a

    3.解压出object file(.o文件):cd armv7&ar xv liblibPDRCore_armv7.a

    4.根据出错信息删除PDRSerAsyncSocket.o:rm PDRSerAsyncSocket.o

    5.重新打包:ar rcs liblibPDRCore_armv7.a *.o,记得把旧的库删掉

    6.重复1-5把arm64什么的一起改了

    7.合并为fat库:lipo -create liblibPDRCore_armv7.a liblibPDRCore_arm64.a -output liblibPDRCore.a

    好了,现在用新和成的库替换,并编译,发现编译成功了!

    且慢!这和主题OC runtime有什么关系?只是用一些工具去掉了重复符号!

    是的,确实没有关系,下面才正式进入主题!

    编译是成功了,但实际运行起来呢?非常抱歉,crash了,错误如下:

    142502_c4CW_2241986.png

    原因可能是:这两个库虽然有一个名字一样的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,果不其然,发现一个类似的,方法名不同,参数名却一样

174851_c4ZK_2241986.jpg

    5.再看一下liblibPDRCore.a库中的PDRSerAsyncSocket.o的导出符号,确实有"acceptOnAddress:port:error:"这个方法,我们知道,这个目标文件已经被我们去掉了

175134_kfqX_2241986.png

    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.没有了,散会

转载于:https://my.oschina.net/u/2241986/blog/500813

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值