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

    前几天在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.没有了,散会

相关文章
相关标签/搜索