應用程序會依賴不少的庫,包括系統的,如UIKit
、CoreFoundation
,還有第三方的。c++
什麼是庫?bootstrap
- 庫是可執行的二進制文件,可以被操做系統加載到內存。
- 庫又分爲靜態庫和動態庫。
在咱們使用斷點斷住程序的時候,xCode
左側Thread1
最下面會調用start
函數這個函數來自libdyld.dylib
緩存
1.下載dyld
最新源碼,目前最新是852
。dyld源碼下載markdown
2.全局搜索_dyld_start
app
3.全局搜索c++
函數的命名空間dyldbootstrap
,在命名空間所在文件搜索start
函數函數
4.在start
函數中最後一步return
到dyld::_main
,進入main
函數。oop
能看到
main
函數佔用了八百多行代碼。這裏不打算一開始就從上往下一行一行分析,而是使用反推法倒着分析,先了解主要流程。源碼分析
1.函數的末尾返回了result
,查看result
在函數體內有哪些賦值操做。post
2.下面兩處都是sMainExecutable
在調用函數的返回值測試
3.查看sMainExecutable
在 _main
函數中的出處
查看初始化函數instantiateFromLoadedImage
返回
machO
讀取對象。
4.連接sMainExecutable
5.弱引用綁定主程序
6.運行全部已經初始化的東西
7.通知主程序進入的main()
函數
主線流程已經分析完,下面跟隨主線流程看看細節
1.首先_main
函數前兩百多行代碼是條件準備,包括環境、平臺、版本、路徑、主機等信息的處理 加載插入的動態庫
2.讀取共享緩存
3.在連接主程序前面,讀取插入的動態庫
4.在連接主程序後面,連接插入的動態庫
插入的動態庫、主程序都調用了
runInitializers
函數
由於gLinkContext.notifySingle = ¬ifySingle;
全局搜索registerObjCNotifiers
的調用
發現是_dyld_objc_notify_register
調用的,並且這個函數是在objc
源碼是函數_objc_init
調用的
接下來在
objc
源碼中_objc_init
打上斷點,打印調用棧
再次使用反推法:
由函數調用棧能看到
libobjc.A.dylib
調用了_objc_init
libdispatch.dylib
調用了_os_object_init
_os_object_init
實現:
_os_object_init
函數內部調用了_objc_init
libdispatch.dylib
調用了libdispatch_init
libdispatch_init
實現:
libdispatch_init
調用了_os_object_init
libSystem.B.dylib
調用了libSystem_initializer
libSystem_initializer
實現:libSystem_initializer
調用了libdispatch_init
dyld ImageLoaderMachO::doModInitFunctions
函數
doModInitFunctions
實現:
也就是說
doModInitFunctions
確保libSystem
初始化
dyld ImageLoaderMachO::doInitialization
doInitialization
實現:
doInitialization
調用了doModInitFunctions
dyld ImageLoader::recursiveInitialization
recursiveInitialization
實現: 這一塊
initializeMainExecutable
分析流程也提到了
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
在_objc_init
中_dyld_objc_notify_register
有三個參數&map_images
、load_images
、unmap_image
, _dyld_objc_notify_register
把參數原封不動的傳給了registerObjCNotifiers
在
registerObjCNotifiers
內部:
sNotifyObjCMapped
=mapped
sNotifyObjCInit
=init
sNotifyObjCUnmapped
=unmapped
map_images
調用就是sNotifyObjCMapped
的調用全局搜索sNotifyObjCMapped
在哪裏調用?
全局搜索notifyBatchPartial
在哪裏調用?
也就是map_images(sNotifyObjCMapped)
在registerObjCNotifiers
->notifyBatchPartial
函數內調用
load_images
調用就是sNotifyObjCInit
的調用全局搜索sNotifyObjCInit
在哪裏調用?
全局搜索notifySingle
在哪裏調用?
測試代碼準備:
int main(int argc, char * argv[]) {
printf("main函數調用 %s \n",__func__);
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
__attribute__((constructor)) void cppFunc(){
printf("C++函數調用 : %s \n",__func__);
}
複製代碼
@implementation ViewController
+ (void)load{
NSLog(@"\n %s 函數調用",__func__);
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
@end
複製代碼
執行結果以下: 調用順序依次爲
load
、c++
函數、main
函數
在_objc_init
調用_dyld_objc_notify_register
,_dyld_objc_notify_register
第二個參數load_images
定義以下:
load
的方法:
load
方法 因此
load
的方法在_objc_init
即將結束時就調用了。
在c++
函數內打上斷點,查看函數調用棧
在調用
doModInitFunctions
後調用了cppFunc
,doModInitFunctions
是讀取machO
的,因此這個c++
函數是寫在machO
中的
在dyld
源碼中搜索_dyld_start
的彙編 在調用完
dyldbootstrap::start
後會調到main
函數
實操驗證:
dyldbootstrap::start
register read
讀取寄存器rax
就是main
函數