iOS App啟動優(yōu)化:動態(tài)庫手動加載

一、前言

在介紹動態(tài)庫手動加載方式之前,我們簡單了解下動態(tài)庫,又名共享庫在iOS中是個特殊的存在,除了系統(tǒng)庫以外,在大部分使用場景下(除了App Extension可以共享)其實并不能達(dá)到共享的目的。在iOS開發(fā)中動態(tài)庫主要有以下用途:

  • 解決蘋果審核iOS8__Text 字段60M限制,將獨立的代碼封裝到動態(tài)庫,進(jìn)而減小可執(zhí)行文件代碼段的大小。

  • 制作第三方庫,因為動態(tài)庫沒有像靜態(tài)庫之間的符號沖突問題(Xcode會有沖突日志,不影響運(yùn)行),很多時候第三方庫往往會以動態(tài)庫的形式存在。

不同于靜態(tài)庫會被一起鏈接到Mach-O文件中,動態(tài)庫是獨立于主程序存在的。我們使用動態(tài)庫時一般是直接拖到工程中,設(shè)置下Embed,使用起來非常方便。這些動態(tài)庫是在App啟動的時候通過dyld(動態(tài)鏈接器)根據(jù)依賴關(guān)系遞歸的加載到內(nèi)存中,這樣的方式稱為動態(tài)庫自動加載。但是如果動態(tài)庫數(shù)量多了,會大大的拖慢應(yīng)用的啟動速度,因為dyld在rebasebinding階段比較耗時。

那么,對于動態(tài)庫使用比較多的項目怎么去優(yōu)化App啟動的耗時呢?其實除了自動加載方式,還有一種是手動加載(也稱為懶加載),我們可以將一些不常用的動態(tài)庫模塊使用手動加載方式。

二、使用

動態(tài)庫手動加載有兩種方式可以實現(xiàn):

  • dlopen;

  • NSBundle load/loadAndReturnError;

蘋果在審核條款中明確禁止使用dlopen(感謝@
iOSLover的分享,和審核團(tuán)隊確認(rèn):加簽過的動態(tài)庫可以使用dlopen。本人未做驗證,僅供參考。),我們重點看下NSBundle load/loadAndReturnError的方式,load的方式底層也是使用dlopen實現(xiàn),只是增加了驗簽,而簽名是在App打包的時候完成。如果從其他途徑(如網(wǎng)絡(luò)下載)獲取的動態(tài)庫是無法完成驗簽的。

手動方式加載方式如下:

  1. 在Build Phases中點擊"+"-"New Copy Files Phase",新增Copy Files選項,如果有動態(tài)庫Strip的腳本,需要將Copy Files拖到前面,保證在打包時可以執(zhí)行去除i386/x86_64指令集;

  2. 修改Copy Files 中的Destination選項為Frameworks,這樣手動加載的動態(tài)庫也會和其他動態(tài)庫拷貝到同一個目錄,點擊"+"-"Add Others..."添加需要手動加載的動態(tài)庫;

    Xcode截圖

現(xiàn)在,我們可以使用了(因為是動態(tài)加載的,調(diào)用方式也只能是動態(tài)調(diào)用):

NSString *path = [[NSBundle mainBundle] pathForResource:@"MyLib" ofType:@"framework" inDirectory:@"Frameworks"];
NSError *err = nil;
NSBundle *bundle =  [NSBundle bundleWithPath:path];
if ([bundle loadAndReturnError:&err]) {
    //加載成功,方法調(diào)用
   Class c = NSClassFromString(@"MyClass");
   [c performSelector:@selector(printLog)];
}
else {
  //加載失敗
}

二、擴(kuò)展

上面的使用方式比較適合沒有依賴的動態(tài)庫。那么,我們能不能將一個業(yè)務(wù)模塊轉(zhuǎn)成動態(tài)庫呢?業(yè)務(wù)模塊往往會依賴各種各樣的庫,如網(wǎng)絡(luò)庫,埋點庫,UI組件庫等等...。而這些庫可能是靜態(tài)庫,也可能是動態(tài)庫。先看下靜態(tài)庫/動態(tài)庫的打包時的依賴的特性:

  • 靜態(tài)庫依賴靜態(tài)庫,只引用,相互獨立;

  • 靜態(tài)庫依賴動態(tài)庫,只引用,相互獨立;

  • 動態(tài)庫依賴動態(tài)庫,只引用,相互獨立;

  • 動態(tài)庫依賴靜態(tài)庫,鏈接到一起;

從上面的特性可以看出,動態(tài)庫如果依賴靜態(tài)庫會“合并”靜態(tài)庫。這樣被依賴的靜態(tài)庫在項目中有多份“拷貝”,這會大大增加包大小。制作動態(tài)庫時可以這樣做:

  1. 在動態(tài)庫工程制作動態(tài)庫的時候,刪除Link Binary With Libraries中依賴的靜態(tài)庫,保留工程目錄下的引用不要刪除;
    Link Binary With Libraries
  1. "other linker Flags" 中添加-undefined dynamic_lookup;
    other linker Flags

這樣打包出來的動態(tài)庫就不會包含靜態(tài)庫了...

因為動態(tài)庫引用了主執(zhí)行文件(靜態(tài)庫最后會被鏈接到主執(zhí)行文件)的符號,所以主工程的配置也需要跟著修改:
"Build Settings"-"Strip Style" 修改為Non-Global Symbols,將外部引用的符號保留,當(dāng)然這會略微增加包大小。

Strip Style

三、加載成功率 & 性能

從線上監(jiān)控數(shù)據(jù)來看未發(fā)現(xiàn)加載失敗的情況,成功率可達(dá)100%。加載耗時跟設(shè)備性能、特別是動態(tài)庫符號(類名,協(xié)議,方法名等)數(shù)量有關(guān)。97% 以上幾乎在用戶無感知的情況下加載完成(毫秒級)。手動加載的效率比自動加載效率低,請勿在app啟動過程中使用。

四、結(jié)束語

以上是動態(tài)庫手動加載的使用方式,隨著越來越多的App放棄iOS8,使用動態(tài)庫來解決__Text 大小限制的需求變得越來越少。但可以作為App啟動優(yōu)化中動態(tài)庫部分的優(yōu)化方案(成本低,效果好)。對于組件化(如CocoaPods)構(gòu)建的工程,上述的配置方案會有不同,但是原理一樣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 動態(tài)鏈接,在可執(zhí)行文件裝載時或運(yùn)行時,由操作系統(tǒng)的裝載程序加載庫。大多數(shù)操作系統(tǒng)將解析外部引用(比如庫)作為加載過...
    小5筒閱讀 5,778評論 0 3
  • sdk開發(fā)筆記基礎(chǔ): 說到動態(tài)庫,就不得不提靜態(tài)庫。靜態(tài)庫可以看做是一個具有特定功能的代碼塊,如果app中引用了靜...
    F麥子閱讀 6,687評論 0 17
  • //聯(lián)系人:石虎QQ: 1224614774昵稱:嗡嘛呢叭咪哄 一 、IOS開發(fā)APP啟動原理 main()函數(shù)是...
    石虎132閱讀 4,175評論 0 20
  • 前面介紹過制作過程,這里不講如何制作動態(tài)庫、靜態(tài)庫。 靜態(tài)庫和動態(tài)庫都是以二進(jìn)制提供代碼復(fù)用的代碼庫。 靜態(tài)庫常見...
    紙簡書生閱讀 23,195評論 10 96
  • 起因 理論功底 動態(tài)庫和靜態(tài)庫 介紹 靜態(tài)庫和動態(tài)庫的區(qū)別 舉個例子, iOS 項目中使用 Embeded Fra...
    leverkusen188閱讀 1,093評論 0 3

友情鏈接更多精彩內(nèi)容