如今的互聯網行業,是一天比一天卷,除了底層是必考點了,還有關於APP
的性能優化也是面試常問的點。 web
在優化以前必需要對
應用程序加載
的流程熟悉,那麼本次博文就對dyld
進行底層的初步探索分析。面試
咱們都知道代碼編寫完成,必須經過編譯器編譯才能變成能夠執行的文件。bootstrap
程序的執行,是把可執行的文件,加載到內存中去執行的,這個可執行的文件(
Mach-O
)的運行必須依賴不少的庫(.a
/.lib
/.so
),庫是可執行的二進制文件,是可以被加載到內存中去的。這些庫,能夠分爲靜態庫
和動態庫
。性能優化
.a
和.framework
。靜態庫連接時,會被完整地複製到可執行文件中,被使用到了屢次,就會複製多份,這樣就有多份拷貝很冗餘,連接時間長了,還浪費了內存空間。.dylib
和.framework
。動態庫連接時,只會存在一份,並不會複製多份,在內存中共享這一份,系統只加載一次,誰有用到了就去找這一份,減小了程序的體積大小,能夠節省時間和內存空間。 靜態庫都好理解,那麼動態庫在程序中是怎麼加載到內存呢?系統是經過怎樣的方式來連接的呢?這就用到了一個工具,也就是博文開頭提到的dyld
(the dynamic link editor
)動態連接器,markdown
dyld
是iOS操做系統的一個重要組成部分,在系統內核作好程序準備工做以後,會交由dyld
負責餘下的工做。dyld
的做用:加載各個庫,也就是image
鏡像文件,由dyld
從內存中讀到表中,加載主程序,link
連接各個動靜態庫,進行主程序的初始化工做。網絡
上面這個圖是
dyld
的加載工做流程圖,圖中簡單的描述了動態庫
的註冊
和動態庫的加載
過程,具體分析還得去看底層源碼,那麼接下來就去探索分析。app
程序的執行咱們都知道是從
main
函數開始的,那麼dyld
在程序的那個階段執行的呢?是在main
函數執行前,仍是再main
函數執行後呢?這我也不得而知。函數
啊!你這個博主,奇奇怪怪的,你寫的博文,你會不知道啊? 這個嘛,就得探索探索了!首先建一個工程將main.m
改寫以下:工具
__attribute__((constructor)) void JPFunc(){
printf("來了老弟 : %s \n",__func__);
}
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
NSLog(@"這是main函數打印");
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
複製代碼
而後運行程序,打印結果以下:oop
來了老弟 : JPFunc
dyld初探[37212:516752] 這是main函數打印
複製代碼
這個__attribute__((constructor))
是在main
函數以前執行的一個函數。
補充:GNU C
的一大特點就是__attribute__
機制。__attribute__
能夠設置函數屬性(Function Attribute
)、變量屬性(Variable Attribute
)和類型屬性(Type Attribute
)。
__attribute__
書寫特徵是:__attribute__
先後都有兩個下劃線,並切後面會緊跟一對原括弧,括弧裏面是相應的__attribute__
參數。
__attribute__
語法格式爲:__attribute__ ((attribute-list))
在
main
函數執行以前確實是能夠執行其餘函數的,那麼dyld
到如今好像尚未相關線索,那麼繼續往下探索。 在main
函數打上斷點
斷點斷在
main
函數上,發如今main
以前還調用了一個start
方法。
點開
start
是一個libdyld.dylib start
,忽然想起網絡上很流行的一句話,歡迎來到德萊聯盟
,libdyld.dylib
和這發音好像,哈哈!
可是經過對start
下符號斷點,斷不住它。說明這不是入口的地方,咱們知道還有一個方法+load
,這個是在main
以前必會調用的方法,那麼就能夠在Viewcontroller
的寫下+load
方法添加斷點,運行程序。在控制檯輸入指令bt
,查看調用堆棧信息:
堆棧信息是一個棧結構,先進後出,因此最底下打印的就是最早執行的。因此如今咱們已經找到
dyld
的入口了。
_dyld_start
,那麼這就涉及到底層源碼了,去蘋果開放的源碼官網opensource
看看dyld源碼
咱們研究源碼,就得去看最新的蘋果源碼,畢竟技術更新迭代很快,最新的纔是最流行的,也是最香的,研究起來纔有味道,
dyld
最新的版本是dyld-852,這部分源碼是不能編譯的,可是並不能妨礙咱們去探索它。那麼咱們如今就去打開dyld
這個牛逼的源碼工程一探究竟吧!
全局搜索
_dyld_start,
發現又是彙編,是否是要瘋了啊!
莫慌靚仔,穩住,不會彙編沒有關係,請耐心往下看!
在彙編裏面發現了一個重要方法,
dyldbootstrap::start
,從紅框中的註釋能夠知道,會調用dyldbootstrap::start
這個C++
函數,那麼就能夠去全局搜索下,看看C++
函數的命名空間。
從命名空間裏面,咱們能夠找到
start
在
start
函數裏面返回的是dyld::_main
,這就nice了,忽然就很熟悉,很親切。
博文篇幅有限,本篇到此結束! 請看下篇iOS底層探索之dyld(下):動態連接器流程源碼分析
dyld
連接各類庫動態庫
和靜態庫
靜態庫
有多份拷貝,增長包的大小,程序加載連接時間長,浪費了內存空間。動態庫
存在一份,並不會複製多份,節省程序加載連接時間和內存空間Mach-O
更多內容持續更新
🌹 喜歡就點個贊吧👍🌹
🌹 以爲學習到了的,能夠來一波,收藏+關注,評論 + 轉發,以避免你下次找不到我😁🌹
🌹歡迎你們留言交流,批評指正,互相學習😁,提高自我🌹