摘要OC runtime真是一個(gè)強(qiáng)大的東西,這次解決了第三方庫(kù)的沖突問(wèn)題
前幾天在iOS app項(xiàng)目中添加了幾個(gè)第三方庫(kù),各有各的用處,因?yàn)橐恍┰颍行?kù)是不開(kāi)源的。
添加后,發(fā)現(xiàn)app編譯不通過(guò),錯(cuò)誤如下:
從錯(cuò)誤描述中都能看出,app在連接過(guò)程中,發(fā)現(xiàn)了一些重復(fù)的符號(hào),即同樣的OC類和方法在不同的庫(kù)中都有實(shí)現(xiàn):liblibPDRCore.a和libsimpleconfiglib.a這兩個(gè)庫(kù)有沖突!恰好,這兩個(gè)庫(kù)都要用,而且都不開(kāi)源,仿佛一下子就走進(jìn)了死胡同,因?yàn)闆](méi)有辦法修改這兩個(gè)庫(kù)。
網(wǎng)上搜了一下,碰到這種問(wèn)題的人還真不少,也提出了解決方案:用lipo命令分解其中一個(gè)類,刪除重復(fù)的符號(hào),再用ar命令重新打包成庫(kù),我選擇修改liblibPDRCore.a這個(gè)庫(kù):
1.查看包信息:lipo -info liblibPDRCore.a,提示fat file,那么代表這個(gè)包是支持多平臺(tái)的,例如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.根據(jù)出錯(cuò)信息刪除PDRSerAsyncSocket.o:rm PDRSerAsyncSocket.o
5.重新打包:ar rcs liblibPDRCore_armv7.a *.o,記得把舊的庫(kù)刪掉
6.重復(fù)1-5把a(bǔ)rm64什么的一起改了
7.合并為fat庫(kù):lipo -create liblibPDRCore_armv7.a liblibPDRCore_arm64.a -output liblibPDRCore.a
好了,現(xiàn)在用新和成的庫(kù)替換,并編譯,發(fā)現(xiàn)編譯成功了!
且慢!這和主題OC runtime有什么關(guān)系?只是用一些工具去掉了重復(fù)符號(hào)!
是的,確實(shí)沒(méi)有關(guān)系,下面才正式進(jìn)入主題!
編譯是成功了,但實(shí)際運(yùn)行起來(lái)呢?非常抱歉,crash了,錯(cuò)誤如下:
原因可能是:這兩個(gè)庫(kù)雖然有一個(gè)名字一樣的OC類,有些方法也一樣,但并不是完全一樣,可以認(rèn)為,這兩個(gè)庫(kù)依賴了另一個(gè)開(kāi)源庫(kù),但不是同一個(gè)版本,仿佛又走進(jìn)死胡同鳥(niǎo)!
仔細(xì)分析了一些出錯(cuò)信息:liblibPDRCore.a某個(gè)地方調(diào)用了AsyncSocket類的方法acceptOnAddress:port:error:,但libsimpleconfiglib.a中的AsyncSocket卻沒(méi)有這個(gè)方法,造成調(diào)用異常。
現(xiàn)在輪到OC runtime上場(chǎng)了,我也只是抱著試一試的態(tài)度。前段時(shí)間正好看這方面內(nèi)容,了解到OC可以在運(yùn)行時(shí)動(dòng)態(tài)增加、替換一些類的方法,解決一些實(shí)際問(wèn)題。現(xiàn)在我想到一個(gè)思路:用一個(gè)空函數(shù),替代AsyncSocket類的方法acceptOnAddress:port:error:,看是否正常運(yùn)行
1.先獲取類類型:Class _asyncSocketClass = NSClassFromString(@"AsyncSocket");
2.給AsyncSocket類添加方法:class_addMethod(_asyncSocketClass, NSSelectorFromString(@"acceptOnAddress:port:error:"), IMP, types);,這個(gè)函數(shù)中,后面兩個(gè)參數(shù),一個(gè)是替換方法,一個(gè)是方法參數(shù)類型,替換方法參數(shù)好搞,直接定義一個(gè)C格式的函數(shù),傳函數(shù)指針即可;方法參數(shù)類型怎么弄?
于是,我想到了從libsimpleconfiglib.a庫(kù)中找一些蛛絲馬跡,雖然它和liblibPDRCore.a使用了不同版本的AsyncSocket類,但某些方法應(yīng)該是類似的,獲取可以幫我完成函數(shù)class_addMethod所需的最后一個(gè)參數(shù)
3.用lipo和ar命令獲得libsimpleconfiglib.a中的object file:"AsyncSocket.o"
4.查看"AsyncSocket.o"的導(dǎo)出符號(hào):nm AsyncSocket.o,果不其然,發(fā)現(xiàn)一個(gè)類似的,方法名不同,參數(shù)名卻一樣
5.再看一下liblibPDRCore.a庫(kù)中的PDRSerAsyncSocket.o的導(dǎo)出符號(hào),確實(shí)有"acceptOnAddress:port:error:"這個(gè)方法,我們知道,這個(gè)目標(biāo)文件已經(jīng)被我們?nèi)サ袅?br>
6.從名稱上判斷,這兩個(gè)方法參數(shù)一樣,而且方法acceptOnInterface確實(shí)存在,用method_getTypeEncoding來(lái)獲取方法的類型即可得到class_addMethod的最后一個(gè)參數(shù),用有了這個(gè)思路,完成了如下代碼:
<pre><code>
void impfunc(Class cls, SEL _cmd){
if([NSStringFromSelector(_cmd) isEqualtoString:@"acceptOnAddress"]) {
//調(diào)用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));
}
</pre></code>
7.編譯并運(yùn)行起來(lái)之后,確實(shí)走到了impfunc,程序也不崩潰了,但相關(guān)的功能不正常,我想還是要真正調(diào)用一下acceptOnInterface這個(gè)方法才行
8.我想到了解析_cmd的參數(shù),取出參數(shù)值,然后再調(diào)用objc_msgSend發(fā)送消息,嘗試了很多方法,沒(méi)有成功;實(shí)際上,我還是走了彎路,各位應(yīng)該也看出來(lái)了,class_addMethod的第三個(gè)參數(shù),直接用acceptOnInterface的實(shí)現(xiàn)不就行了!method_getImplementation可以幫我們做到,于是代碼變成如下,編譯后運(yùn)行,成功了!
- (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.沒(méi)有了,散會(huì)