iOS App啟動優化:動態庫手動加載

一、前言

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

  • 解決蘋果審核iOS8__Text 字段60M限制,將獨立的代碼封裝到動態庫,進而減小可執行文件代碼段的大小。

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

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

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

二、使用

動態庫手動加載有兩種方式可以實現:

  • dlopen;

  • NSBundle load/loadAndReturnError;

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

手動方式加載方式如下:

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

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

    Xcode截圖

現在,我們可以使用了(因為是動態加載的,調用方式也只能是動態調用):

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

二、擴展

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

  • 靜態庫依賴靜態庫,只引用,相互獨立;

  • 靜態庫依賴動態庫,只引用,相互獨立;

  • 動態庫依賴動態庫,只引用,相互獨立;

  • 動態庫依賴靜態庫,鏈接到一起;

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

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

這樣打包出來的動態庫就不會包含靜態庫了...

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

Strip Style

三、加載成功率 & 性能

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

四、結束語

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

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