前沿
我們實現ViewController的+(void)load方法萌狂,在main函數中添加c++方法
//ViewController的load方法
+ (void)load{
NSLog(@"%s",__func__);
}
//程序入口
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
NSLog(@"1223333");
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
//c++方法
__attribute__((constructor)) void kcFunc(){
printf("來了 : %s \n",__func__);
}
打印結果
2020-09-29 15:13:42.259577+0800 002-應用程加載分析[81605:302871] +[ViewController load]
來了 : kcFunc
2020-09-29 15:13:42.268476+0800 002-應用程加載分析[81605:302871] 1223333
我們可以看出先load然后c++竿痰,最后main方法凡傅,接下來我們圍繞這個打印順序就行分析角寸。
我們在c++方法處打斷點专筷,然后查看堆棧信息如下
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x0000000102858174 002-應用程加載分析`kcFunc at main.m:30:5
frame #1: 0x00000001028786d9 dyld_sim`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 513
frame #2: 0x0000000102878ace dyld_sim`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 40
frame #3: 0x0000000102873868 dyld_sim`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 456
frame #4: 0x0000000102871d2c dyld_sim`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188
frame #5: 0x0000000102871dcc dyld_sim`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82
frame #6: 0x0000000102866270 dyld_sim`dyld::initializeMainExecutable() + 199
frame #7: 0x000000010286a1bb dyld_sim`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 3662
frame #8: 0x00000001028651cd dyld_sim`start_sim + 122
frame #9: 0x0000000108e4e85c dyld`dyld::useSimulatorDyld(int, macho_header const*, char const*, int, char const**, char const**, char const**, unsigned long*, unsigned long*) + 2308
frame #10: 0x0000000108e4c4f4 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 837
frame #11: 0x0000000108e47227 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 453
frame #12: 0x0000000108e47025 dyld`_dyld_start + 37
編譯過程
我們寫的oc代碼怎么能顯示到手機上呢于未?
我們寫的hm文件先要經過預編譯撕攒,預編譯主要是語法分析,語義分析烘浦,編譯是生成中間代碼然后匯編鏈接到可執(zhí)行文件
iOS的庫
靜態(tài)庫通常以.a抖坪,.lib或者.framework結尾,動態(tài)庫以.tbd闷叉,.so擦俐,.framework結尾
1.靜態(tài)庫:鏈接時,靜態(tài)庫會被完整的復制到可執(zhí)行文件中握侧,被多次使用就會有多份冗余拷貝
2.動態(tài)庫:鏈接時不復制蚯瞧,程序運行時由系統(tǒng)動態(tài)加載到內存嘿期,供程序調用,系統(tǒng)只加載一次埋合,多個程序公用备徐,節(jié)省內存
我們通過一個圖來區(qū)分靜態(tài)庫和動態(tài)庫
在靜態(tài)庫中B和D分別被完整的復制到可執(zhí)行文件,而動態(tài)庫中則由系統(tǒng)動態(tài)加載到內存甚颂。
dyld加載流程
我們要先在官網上下載一份dyld源碼
概念什么是dyld
dyld 是英文 the dynamic link editor 的簡寫蜜猾,翻譯過來就是動態(tài)鏈接器,是蘋果操作系統(tǒng)的一個重要的組成部分振诬。在 iOS/Mac OSX 系統(tǒng)中蹭睡,僅有很少量的進程只需要內核就能完成加載,基本上所有的進程都是動態(tài)鏈接的贷揽,所以 Mach-O 鏡像文件中會有很多對外部的庫和符號的引用棠笑,但是這些引用并不能直接用梦碗,在啟動時還必須要通過這些引用進行內容的填補禽绪,這個填補工作就是由 動態(tài)鏈接器dyld 來完成的,也就是符號綁定洪规。動態(tài)鏈接器dyld 在系統(tǒng)中以一個用戶態(tài)的可執(zhí)行文件形式存在印屁,一般應用程序會在 Mach-O 文件部分指定一個 LC_LOAD_DYLINKER 的加載命令,此加載命令指定了 dyld 的路徑斩例,通常它的默認值是 /usr/lib/dyld 雄人。系統(tǒng)內核在加載 Mach-O 文件時,都需要用 dyld(位于 /usr/lib/dyld )程序進行鏈接念赶。
總括加載流程
_dyld_start源碼
__dyld_start:
...
//調用dyldbootstrap:start函數
# call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
subl $L__dyld_start_picbase-__dyld_start, %ebx # ebx = &__dyld_start
subl $0x1000, %ebx # ebx = load address of dyld
movl %edx,(%esp) # param1 = app_mh
movl 4(%ebp),%eax
movl %eax,4(%esp) # param2 = argc
lea 8(%ebp),%eax
movl %eax,8(%esp) # param3 = argv
movl %ebx,12(%esp) # param4 = dyld load address
lea 28(%esp),%eax
movl %eax,16(%esp) # param5 = &startGlue
call __ZN13dyldbootstrap5startEPKN5dyld311MachOLoadedEiPPKcS3_Pm
movl 28(%esp),%edx
cmpl $0,%edx
jne Lnew
...
//調用main函數
# LC_MAIN case, set up stack for call to main()
Lnew: movl 4(%ebp),%ebx
movl %ebx,(%esp) # main param1 = argc
leal 8(%ebp),%ecx
movl %ecx,4(%esp) # main param2 = argv
leal 0x4(%ecx,%ebx,4),%ebx
movl %ebx,8(%esp) # main param3 = env
...
_dyld_start=====>dyldbootstrap::start======>main
dyldbootstrap::start最主要的就是調用dyld::_main
dyldbootstrap::start===>dyld::_main
dyld::_main流程
我們可以根據注釋去了解大致流程础钠,我們最主要研究的是initializeMainExecutable函數
initializeMainExecutable
initializeMainExecutable=====>runInitializers=====>processInitializers=======>recursiveInitialization
recursiveInitialization
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// initialize this image
bool hasInitializers = this->doInitialization(context);
context.notifySingle中
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
我們追蹤sNotifyObjCInit發(fā)現賦值
_dyld_objc_notify_register====> dyld::registerObjCNotifiers(mapped, init, unmapped);==>(sNotifyObjCInit = init;)
在objc源碼中搜索_dyld_objc_notify_register,發(fā)現此方法是在_objc_init中調用的
并且sNotifyObjCInit的回調實現為
調用load方法
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
我們在_objc_init打斷點
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x00000001002d461d libobjc.A.dylib`_objc_init at objc-os.mm:920:5
frame #1: 0x000000010042b0bc libdispatch.dylib`_os_object_init + 13
frame #2: 0x000000010043bafc libdispatch.dylib`libdispatch_init + 282
frame #3: 0x00007fff6ad08791 libSystem.B.dylib`libSystem_initializer + 220
frame #4: 0x000000010002f1d3 dyld`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 535
frame #5: 0x000000010002f5de dyld`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 40
frame #6: 0x0000000100029ffb dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 493
frame #7: 0x0000000100029f66 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 344
frame #8: 0x00000001000280b4 dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188
frame #9: 0x0000000100028154 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82
frame #10: 0x0000000100016662 dyld`dyld::initializeMainExecutable() + 129
frame #11: 0x000000010001bbba dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 6667
frame #12: 0x0000000100015227 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 453
frame #13: 0x0000000100015025 dyld`_dyld_start + 37
系統(tǒng)會優(yōu)先初始化libSystem叉谜,libdispatch旗吁,libdispatch libobjc,然后注冊一個通知sNotifyObjCInit停局,當我們初始化主程序或者我們寫的庫時很钓,會發(fā)通知讓先加載該哭的load方法,然后doInitialization進行調用c++方法