使用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.沒有了,散會

相關文章
相關標籤/搜索