目錄
- 對(duì)Objective-C語言的理解
- iOS 程序 main 函數(shù)之前發(fā)生了什么
- 靜態(tài)庫與動(dòng)態(tài)庫
- 系統(tǒng)框架圖
1. 對(duì)Objective-C語言的理解
編程的核心
編程的無非兩件事,數(shù)據(jù)和運(yùn)算。
放在計(jì)算機(jī)硬件,是內(nèi)存和CPU;
放在C語言,是結(jié)構(gòu)體和函數(shù)(基本類型本質(zhì)上就是一個(gè)只有一個(gè)字段的結(jié)構(gòu)體);
放在面向?qū)ο蟮恼Z言,是類和消息;
放在函數(shù)式語言,就是值和函數(shù)了
objc所有類和對(duì)象都是c結(jié)構(gòu)體,category當(dāng)然也一樣
oc關(guān)于對(duì)象模型的設(shè)計(jì)
消息機(jī)制就不一樣了,要實(shí)現(xiàn)向一個(gè) target ( class / instance ) 發(fā)送消息名 ( selector ) 動(dòng)態(tài)尋找到函數(shù)實(shí)現(xiàn)地址 ( IMP ) 并調(diào)用的過程,還要處理消息向父類傳遞、消息轉(zhuǎn)發(fā)( Smalltalk 中叫 “Message-Not-Understood”)等,
便形成了最原始的 Runtime。
所以最初的 Objective-C = C + Preprocessor(預(yù)處理器,Clang) + Runtime
Runtime 中只要實(shí)現(xiàn)幾個(gè)最基礎(chǔ)的函數(shù)(如 objc_msgSend)即可
Runtime 的精髓并非在于平日里很少接觸的那些所謂“黑魔法” Runtime API、也并非各種 Swizzle 大法
2. iOS 程序 main 函數(shù)之前發(fā)生了什么
一個(gè) iOS App 的 main 函數(shù)位于 main.m 中,這是我們熟知的程序入口。但對(duì) objc 了解更多之后發(fā)現(xiàn),程序在進(jìn)入我們的 main 函數(shù)前已經(jīng)執(zhí)行了很多代碼,比如熟知的 + load 方法等。本文將跟隨程序執(zhí)行順序,刨根問底,從 dyld 到 runtime ,看看 main 函數(shù)之前都發(fā)生了什么。
動(dòng)態(tài)鏈接庫
iOS 中用到的所有系統(tǒng) framework 都是動(dòng)態(tài)鏈接的,類比成插頭和插排,靜態(tài)鏈接的代碼在編譯后的靜態(tài)鏈接過程就將插頭和插排一個(gè)個(gè)插好,運(yùn)行時(shí)直接執(zhí)行二進(jìn)制文件;而動(dòng)態(tài)鏈接需要在程序啟動(dòng)時(shí)去完成“插插銷”的過程,所以在我們寫的代碼執(zhí)行前,動(dòng)態(tài)連接器需要完成準(zhǔn)備工作。
這些 framework 將會(huì)在動(dòng)態(tài)鏈接過程中被加載
清楚的看到整個(gè)調(diào)用棧和順序:
dyld 開始將程序二進(jìn)制文件初始化
交由 ImageLoader 讀取 image,其中包含了我們的類、方法等各種符號(hào)
由于 runtime 向 dyld 綁定了回調(diào),當(dāng) image 加載到內(nèi)存后,dyld 會(huì)通知 runtime 進(jìn)行處理
runtime 接手后調(diào)用 map_images 做解析和處理,接下來 load_images 中調(diào)用 call_load_methods 方法,遍歷所有加載進(jìn)來的 Class,按繼承層級(jí)依次調(diào)用 Class 的 +load 方法和其 Category 的 +load 方法
至此,可執(zhí)行文件中和動(dòng)態(tài)庫所有的符號(hào)(Class,Protocol,Selector,IMP,…)都已經(jīng)按格式成功加載到內(nèi)存中,被 runtime 所管理,再這之后,runtime 的那些方法(動(dòng)態(tài)添加 Class、swizzle 等等才能生效)
簡單總結(jié)
整個(gè)事件由 dyld (動(dòng)態(tài)鏈接器)主導(dǎo),完成運(yùn)行環(huán)境的初始化后,配合 ImageLoader 將二進(jìn)制文件按格式加載到內(nèi)存,
動(dòng)態(tài)鏈接依賴庫,并由 runtime 負(fù)責(zé)加載成 objc 定義的結(jié)構(gòu),所有初始化工作結(jié)束后,dyld 調(diào)用真正的 main 函數(shù)。
值得說明的是,這個(gè)過程遠(yuǎn)比寫出來的要復(fù)雜,這里只提到了 runtime 這個(gè)分支,還有像 GCD、XPC 等重頭的系統(tǒng)庫初始化分支沒有提及(當(dāng)然,有緩存機(jī)制在,它們也不會(huì)玩命初始化),總結(jié)起來就是 main 函數(shù)執(zhí)行之前,系統(tǒng)做了茫茫多的加載和初始化工作,但都被很好的隱藏了,我們無需關(guān)心。
3. 靜態(tài)庫與動(dòng)態(tài)庫
庫簡介:
- 庫是共享程序代碼的方式,一般分為靜態(tài)庫和動(dòng)態(tài)庫。
- 庫作用:模塊化,方便共享代碼,便于合理使用,開發(fā)第三方sdk,別人看不到源碼
- 蘋果不讓使用自己的動(dòng)態(tài)庫,否則審核就無法通過
區(qū)別:
靜態(tài)庫:
- 鏈接時(shí)完整地拷貝至可執(zhí)行文件中,被多次使用就有多份冗余拷貝。
- .a和.framework(自己制造的)
動(dòng)態(tài)庫:
- 鏈接時(shí)不復(fù)制,程序運(yùn)行時(shí)由系統(tǒng)動(dòng)態(tài)加載到內(nèi)存,供程序調(diào)用,系統(tǒng)只加載一次,多個(gè)程序共用,節(jié)省內(nèi)存。
- .tbd(以前叫.dylib)和.framework(系統(tǒng))
a與.framework區(qū)別:
.a + .h + sourceFile = .framework
- .a是一個(gè)純二進(jìn)制文件,不能直接使用,要有.h文件配合
- .framework中除了有二進(jìn)制文件之外還有資源文件, 可以直接使用。
指令集:
平時(shí)項(xiàng)目開發(fā)中,可能使用第三方提供的靜態(tài)庫.a,如果.a提供方技術(shù)不成熟,使用的時(shí)候就會(huì)出現(xiàn)問題,例如:
在真機(jī)上編譯報(bào)錯(cuò):No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=i386).
在模擬器上編譯報(bào)錯(cuò):No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=armv7s, VALID_ARCHS=armv7 armv6).
要解決以上問題,就要了解一下Apple移動(dòng)設(shè)備處理器指令集相關(guān)的一些細(xì)節(jié)知識(shí)。
ARM
ARM處理器,特點(diǎn)是體積小、低功耗、低成本、高性能,所以幾乎所有手機(jī)處理器都基于ARM,在嵌入式系統(tǒng)中應(yīng)用廣泛。
ARM處理器指令集
- iPhone處理器的指令集:
armv6|armv7|armv7s|arm64都是ARM處理器的指令集,這些指令集都是向下兼容的,例如armv7指令集兼容armv6,只是使用armv6的時(shí)候無法發(fā)揮出其性能,無法使用armv7的新特性,從而會(huì)導(dǎo)致程序執(zhí)行效率沒那么高。 機(jī)器對(duì)指令集的支持是向下兼容的,因此armv7的指令集是可以運(yùn)行在iphone5S的,只是效率沒那么高而已。 - Mac處理器的指令集:
i386|x86_64 ,i386是針對(duì)intel通用微處理器32架構(gòu)的。x86_64是針對(duì)x86架構(gòu)的64位處理器。所以當(dāng)使用iOS模擬器的時(shí)候會(huì)遇到i386|x86_64,iOS模擬器沒有arm指令集。
目前iOS移動(dòng)設(shè)備指令集
arm64: iPhone7 | iPhone6 |iPhone5S| iPad Air| iPad mini2(iPad mini with Retina Display)
armv7s:iPhone5|iPhone5C|iPad4(iPad with Retina Display)
armv7:iPhone3GS|iPhone4|iPhone4S|iPad|iPad2|iPad3(The New iPad)|iPad mini|iPod Touch 3G|iPod Touch4
armv6 設(shè)備: iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch(一般不需要去支持)
制作靜態(tài)庫:(后續(xù)...)
- .a文件
- .framework文件
4. 系統(tǒng)框架圖
