靜態(tài)庫(kù)的原理是什么?你有沒(méi)有自己寫過(guò)靜態(tài)編譯庫(kù)舌仍,遇到了哪些問(wèn)題妒貌?
庫(kù)本質(zhì)上講是一種可執(zhí)行的二進(jìn)制格式,可以載入內(nèi)存中執(zhí)行铸豁。是程序代碼的集合灌曙,共享代碼的一種方式。
靜態(tài)庫(kù)是閉源庫(kù)节芥,不公開源代碼在刺,都是編譯后的二進(jìn)制文件,不暴露具體實(shí)現(xiàn)。
靜態(tài)庫(kù) 一般都是以 .a 或者 .framework 形式存在。
靜態(tài)庫(kù)編譯的文件比較大,因?yàn)檎麄€(gè)函數(shù)庫(kù)的數(shù)據(jù)都會(huì)被整合到代碼中畜吊,這樣的好處就是編譯后的程序不需要外部的函數(shù)庫(kù)支持奶赠,不好的一點(diǎn)就是如果改變靜態(tài)函數(shù)庫(kù),就需要程序重新編譯。多次使用就有多份冗余拷貝。
使用靜態(tài)庫(kù)的好處:模塊化分工合作、可重用队丝、避免少量改動(dòng)導(dǎo)致大量的重復(fù)編譯鏈接。
一般公司都會(huì)有核心開發(fā)團(tuán)隊(duì)和普通開發(fā)團(tuán)隊(duì)欲鹏,然后公司的核心業(yè)務(wù)由核心開發(fā)團(tuán)隊(duì)寫成靜態(tài)庫(kù)然后讓普通開發(fā)團(tuán)隊(duì)調(diào)用机久,這樣就算普通開發(fā)團(tuán)隊(duì)離職也帶不走公司的核心業(yè)務(wù)代碼。一般核心開發(fā)團(tuán)隊(duì)是不會(huì)離職的赔嚎。
有靜態(tài)庫(kù)自然就有動(dòng)態(tài)庫(kù)了膘盖。這里所謂的靜態(tài)和動(dòng)態(tài)是相對(duì)編譯期和運(yùn)行期的。靜態(tài)庫(kù)在程序編譯時(shí)會(huì)被鏈接到代碼中尤误,程序運(yùn)行時(shí)將不再需要改靜態(tài)庫(kù)侠畔,而動(dòng)態(tài)庫(kù)在編譯時(shí)不會(huì)被鏈接到代碼中,只有程序運(yùn)行時(shí)才會(huì)被載入损晤,所以 hook 別人程序或者說(shuō)做插件都是運(yùn)用了 runtime 機(jī)制软棺,然后動(dòng)態(tài)庫(kù)注入修改的。
制作 .a 文件時(shí)候尤勋,要注意 CPU 架構(gòu)的支持喘落,i386、X86_64最冰、 armv7瘦棋、armv7s。 查看可以通過(guò)命令 “ lipo -info 靜態(tài)庫(kù)名稱” 暖哨。模擬器 .a 文件和真機(jī) .a 文件合并可以通過(guò) "lipo -create 模擬器靜態(tài)庫(kù)1名 真機(jī)靜態(tài)庫(kù)2名 -output 新靜態(tài)庫(kù)名稱"
一些坑
命名不要太隨意赌朋,畢竟是被別人拿過(guò)去用的要能看懂。
framework中用到了NSClassFromString鹿蜀,但是轉(zhuǎn)換出來(lái)的class 一直為nil箕慧。解決方法:在主工程的【Other Linker Flags】需要添加參數(shù)【-ObjC]即可。
如果Xcode找不到框架的頭文件茴恰,你可能是忘記將它們聲明為public了。
解決方法:進(jìn)入target的Build Phases頁(yè)斩熊,展開Copy Headers項(xiàng)往枣,把需要public的頭文件從Project或Private部分拖拽到Public部分。
盡量不要用 xib 由于靜態(tài)框架采用靜態(tài)鏈接,linker會(huì)剔除所有它認(rèn)為無(wú)用的代碼分冈。不幸的是圾另,linker不會(huì)檢查xib文件,因此如果類是在xib中引用雕沉,而沒(méi)有在O-C代碼中引用集乔,linker將從最終的可執(zhí)行文件中刪除類。這是linker的問(wèn)題坡椒,不是框架的問(wèn)題(當(dāng)你編譯一個(gè)靜態(tài)庫(kù)時(shí)也會(huì)發(fā)生這個(gè)問(wèn)題)扰路。蘋果內(nèi)置框架不會(huì)發(fā)生這個(gè)問(wèn)題,因?yàn)樗麄兪沁\(yùn)行時(shí)動(dòng)態(tài)加載的倔叼,存在于iOS設(shè)備固件中的動(dòng)態(tài)庫(kù)是不可能被刪除的汗唱。
有兩個(gè)解決的辦法:
1、 讓框架的最終用戶關(guān)閉linker的優(yōu)化選項(xiàng)丈攒,通過(guò)在他們的項(xiàng)目的Other Linker Flags中添加-ObjC和-all_load哩罪。
2、 在框架的另一個(gè)類中加一個(gè)該類的代碼引用巡验。例如际插,假設(shè)你有個(gè)MyTextField類,被linker剔除了显设。假設(shè)你還有一個(gè)MyViewController腹鹉,它在xib中使用了MyTextField,MyViewController并沒(méi)有被剔除敷硅。你應(yīng)該這樣做:
在MyTextField中:
+(void)forceLinkerLoad_ {}
在MyViewController中:
+(void)initialize {[MyTextField forceLinkerLoad_];}
他們?nèi)匀恍枰砑?ObjC到linker設(shè)置功咒,但不需要強(qiáng)制all_load了。
第2種方法需要你多做一點(diǎn)工作绞蹦,但卻讓最終用戶避免在使用你的框架時(shí)關(guān)閉linker優(yōu)化(關(guān)閉linker優(yōu)化會(huì)導(dǎo)致object文件膨脹)力奋。
id和NSObject*的區(qū)別
id是一個(gè) objc_object 結(jié)構(gòu)體指針,定義是
typedef struct objc_object *id
id可以理解為指向?qū)ο蟮闹羔樣钠摺K衞c的對(duì)象 id都可以指向景殷,編譯器不會(huì)做類型檢查,id調(diào)用任何存在的方法都不會(huì)在編譯階段報(bào)錯(cuò)澡屡,當(dāng)然如果這個(gè)id指向的對(duì)象沒(méi)有這個(gè)方法猿挚,該崩潰還是會(huì)崩潰的。
NSObject *指向的必須是NSObject的子類驶鹉,調(diào)用的也只能是NSObjec里面的方法否則就要做強(qiáng)制類型轉(zhuǎn)換绩蜻。
不是所有的OC對(duì)象都是NSObject的子類,還有一些繼承自NSProxy室埋。NSObject *可指向的類型是id的子集办绝。