一、前言
在介紹動態庫手動加載方式之前,我們簡單了解下動態庫,又名共享庫在iOS中是個特殊的存在,除了系統庫以外,在大部分使用場景下(除了App Extension可以共享)其實并不能達到共享的目的。在iOS開發中動態庫主要有以下用途:
解決蘋果審核iOS8
__Text
字段60M限制,將獨立的代碼封裝到動態庫,進而減小可執行文件代碼段的大小。制作第三方庫,因為動態庫沒有像靜態庫之間的符號沖突問題(Xcode會有沖突日志,不影響運行),很多時候第三方庫往往會以動態庫的形式存在。
不同于靜態庫會被一起鏈接到Mach-O文件中,動態庫是獨立于主程序存在的。我們使用動態庫時一般是直接拖到工程中,設置下Embed,使用起來非常方便。這些動態庫是在App啟動的時候通過dyld(動態鏈接器)根據依賴關系遞歸的加載到內存中,這樣的方式稱為動態庫自動加載。但是如果動態庫數量多了,會大大的拖慢應用的啟動速度,因為dyld在rebase
和binding
階段比較耗時。
那么,對于動態庫使用比較多的項目怎么去優化App啟動的耗時呢?其實除了自動加載方式,還有一種是手動加載(也稱為懶加載),我們可以將一些不常用的動態庫模塊使用手動加載方式。
二、使用
動態庫手動加載有兩種方式可以實現:
dlopen;
NSBundle load/loadAndReturnError;
蘋果在審核條款中明確禁止使用dlopen(感謝@
iOSLover的分享,和審核團隊確認:加簽過的動態庫可以使用dlopen。本人未做驗證,僅供參考。),我們重點看下NSBundle load/loadAndReturnError的方式,load的方式底層也是使用dlopen實現,只是增加了驗簽,而簽名是在App打包的時候完成。如果從其他途徑(如網絡下載)獲取的動態庫是無法完成驗簽的。
手動方式加載方式如下:
在Build Phases中點擊"+"-"New Copy Files Phase",新增
Copy Files
選項,如果有動態庫Strip的腳本,需要將Copy Files
拖到前面,保證在打包時可以執行去除i386/x86_64指令集;-
修改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組件庫等等...。而這些庫可能是靜態庫,也可能是動態庫。先看下靜態庫/動態庫的打包時的依賴的特性:
靜態庫依賴靜態庫,只引用,相互獨立;
靜態庫依賴動態庫,只引用,相互獨立;
動態庫依賴動態庫,只引用,相互獨立;
動態庫依賴靜態庫,鏈接到一起;
從上面的特性可以看出,動態庫如果依賴靜態庫會“合并”靜態庫。這樣被依賴的靜態庫在項目中有多份“拷貝”,這會大大增加包大小。制作動態庫時可以這樣做:
- 在動態庫工程制作動態庫的時候,刪除
Link Binary With Libraries
中依賴的靜態庫,保留工程目錄下的引用不要刪除;
Link Binary With Libraries
- "other linker Flags" 中添加
-undefined dynamic_lookup
;
other linker Flags
這樣打包出來的動態庫就不會包含靜態庫了...
因為動態庫引用了主執行文件(靜態庫最后會被鏈接到主執行文件)的符號,所以主工程的配置也需要跟著修改:
"Build Settings"-"Strip Style" 修改為Non-Global Symbols
,將外部引用的符號保留,當然這會略微增加包大小。
三、加載成功率 & 性能
從線上監控數據來看未發現加載失敗的情況,成功率可達100%。加載耗時跟設備性能、特別是動態庫符號(類名,協議,方法名等)數量有關。97% 以上幾乎在用戶無感知的情況下加載完成(毫秒級)。手動加載的效率比自動加載效率低,請勿在app啟動過程中使用。
四、結束語
以上是動態庫手動加載的使用方式,隨著越來越多的App放棄iOS8,使用動態庫來解決__Text
大小限制的需求變得越來越少。但可以作為App啟動優化中動態庫部分的優化方案(成本低,效果好)。對于組件化(如CocoaPods)構建的工程,上述的配置方案會有不同,但是原理一樣。