main()方法調(diào)用前,啟動過程大體分為如下步驟:
1.內(nèi)核加載可執(zhí)行文件
2.load dylibs image (加載程序所需的動態(tài)庫鏡像文件)
3.Rebase image / Bind image (由于ASLR(address space layout randomization)的存在,可執(zhí)行文件和動態(tài)鏈接庫在虛擬內(nèi) 存中的加載地址每次啟動都不固定,所以需要修復(fù)鏡像中的資源指針)
4.Objc setup (注冊O(shè)bjc類、將Category中的方法插入方法列表)
5.initializers (調(diào)用Objc類的+load()方法、調(diào)用C++類的構(gòu)造函數(shù))
APP的啟動可以分為2種
冷啟動(Cold Launch):從零開始啟動APP;
熱啟動(Warm Launch):APP已經(jīng)在內(nèi)存中,在后臺存活著,再次點擊圖標(biāo)啟動APP。
APP的冷啟動可以概括為3大階段
dyld(dynamic link editor):
Apple的動態(tài)鏈接器,可以用來裝載Mach-O文件(可執(zhí)行文件、動態(tài)庫等)
runtime;
main。
- dyld 階段:
裝載APP的可執(zhí)行(Mach-o)文件,同時會遞歸加載所有依賴的動態(tài)庫;
當(dāng)dyld把可執(zhí)行文件、動態(tài)庫都裝載完畢后,會通知Runtime進(jìn)行下一步的處理。
- runtime 階段:
調(diào)用map_images進(jìn)行可執(zhí)行文件內(nèi)容的解析和處理:
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
2.在load_images中調(diào)用call_load_methods,調(diào)用所有Class和Category的+load方法;
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
進(jìn)行各種objc結(jié)構(gòu)的初始化(注冊O(shè)bjc類 、初始化類對象等等)
調(diào)用C++靜態(tài)初始化器和attribute((constructor))修飾的函數(shù)
到此為止,可執(zhí)行文件和動態(tài)庫中所有的符號(Class,Protocol,Selector,IMP,…)都已經(jīng)按格式成功加載到內(nèi)存中,被runtime 所管理。
- main函數(shù)啟動階段
所有初始化工作結(jié)束后,dyld就會調(diào)用main函數(shù);
接下來就是UIApplicationMain函數(shù),AppDelegate的application:didFinishLaunchingWithOptions:方法
冷啟動優(yōu)化:
- dyld階段
減少動態(tài)庫、合并一些動態(tài)庫(定期清理不必要的動態(tài)庫);
減少Objc類、分類的數(shù)量、減少Selector數(shù)量(定期清理不必要的類、分類);
減少C++虛函數(shù)數(shù)量;(虛函數(shù)的存在,會生成一張?zhí)摫恚?br>
Swift盡量使用struct。
- runtime階段
用+initialize方法和dispatch_once取代所有的attribute((constructor))、C++靜態(tài)構(gòu)造器、ObjC的+load。
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
}
- main
在不影響用戶體驗的前提下,盡可能將一些操作延遲,不要全部都放在finishLaunching方法中;
按需加載。