iOS多工程聯(lián)編

我們講解多工程聯(lián)編時,首先要明白為什么要多工程聯(lián)編,怎么多工程聯(lián)編,多工程聯(lián)編的優(yōu)劣點
一.首先我們介紹一下為什么要多工程聯(lián)編
因為公司的項目越來越大了,業(yè)務端代碼都是混亂管理,造成開發(fā)有很多痛點無法單測,無法統(tǒng)一管理同功能模塊,快速定位bug,維護成本大,效率低,團隊成員提交代碼沖突機率大,CI配合效果差,功能性代碼多端無法復用,編譯時間長 等等痛點,所以要將項目進行模塊化,組件化管理,同一個模塊封裝成一個工程,靜態(tài)庫,維護起來就高效多.下次在開發(fā)新項目時,就可以把這個工程引入進來,要用里面的功能時就可以直接使用,要我們維護好這個靜態(tài)庫工程,以后開發(fā)就會省事不少。
二.怎么創(chuàng)建多工程
對一個項目來說,將封裝好的靜態(tài)庫直接拖到主工程里,引入頭文件,詳細可以網(wǎng)上搜索
三.多工程聯(lián)編的優(yōu)劣點
這個是重點,有關編譯效率問題,性能問題,如果不好,比之前還慢,那就沒有必要了,
1.APP打包運行原理:Objective和Swift都是編譯語言,換句話說都是需要編譯才能執(zhí)行的
不管是OC還是Swift,都是采用Clang作為編譯器前端,LLVM(Low level vritual machine)作為編譯器后端。所以簡單的編譯過程如圖


D42876FB-39BB-4558-B07A-A8A68E922B61.png

編譯器前端:編譯器前端的任務是進行:語法分析,語義分析,生成中間代碼(intermediate representation )。在這個過程中,會進行類型檢查,如果發(fā)現(xiàn)錯誤或者警告會標注出來在哪一行

編譯器后端:編譯器后端會進行機器無關的代碼優(yōu)化,生成機器語言,并且進行機器相關的代碼優(yōu)化

執(zhí)行一次XCode build的流程:當你在XCode中,選擇build的時候(快捷鍵command+B),會執(zhí)行如下過程

1),編譯信息寫入輔助文件,創(chuàng)建編譯后的文件架構(name.app)
2),處理文件打包信息
3),執(zhí)行CocoaPod編譯前腳本,例如對于使用CocoaPod的工程會執(zhí)行CheckPods Manifest.lock
4),編譯各個.m文件,使用CompileC和clang命令
5),鏈接需要的Framework,例如Foundation.framework,AFNetworking.framework,ALiPay.fframework
6),編譯xib文件
7),拷貝xib,圖片等資源文件到結果目錄
8),編譯ImageAssets
9),處理info.plist
10),拷貝Swift標準庫
11),創(chuàng)建.app文件和對其簽名
我們在每次編譯過后,都會生成一個dsym文件。dsym文件中,存儲了16進制的函數(shù)地址映射。在App實際執(zhí)行的二進制文件中,是通過地址來調用方法的
在APP啟動運行時:
系統(tǒng)首先加載可執(zhí)行文件(自身App的所有.o文件的集合),然后加載動態(tài)鏈接庫dyld,dyld是一個專門用來加載動態(tài)鏈接庫的庫。 執(zhí)行從dyld開始,dyld從可執(zhí)行文件的依賴開始, 遞歸加載所有的依賴動態(tài)鏈接庫。
動態(tài)鏈接庫包括:iOS 中用到的所有系統(tǒng) framework,加載OC runtime方法的libobjc,系統(tǒng)級別的libSystem,例如libdispatch(GCD)和libsystem_blocks (Block)。
其實無論對于系統(tǒng)的動態(tài)鏈接庫還是對于App本身的可執(zhí)行文件而言,他們都算是image(鏡像),而每個App都是以image(鏡像)為單位進行加載的
image究竟包括哪些呢?
1).executable可執(zhí)行文件 比如.o文件。
2).dylib 動態(tài)鏈接庫 framework就是動態(tài)鏈接庫和相應資源包含在一起的一個文件夾結構。
3).bundle 資源文件 只能用dlopen加載,不推薦使用這種方式加載。


6C48480E-2FB8-4150-BEFC-E937BA13D2EB.png

如上圖所示,不同進程之間共用系統(tǒng)dylib的_TEXT區(qū),但是各自維護對應的_DATA區(qū)
所有動態(tài)鏈接庫和我們App中的靜態(tài)庫.a和所有類文件編譯后的.o文件最終都是由dyld(the dynamic link editor),Apple的動態(tài)鏈接器來加載到內存中。每個image都是由一個叫做ImageLoader的類來負責加載(一一對應)
什么是ImageLoader
image 表示一個二進制文件(可執(zhí)行文件或 so 文件),里面是被編譯過的符號、代碼等,所以 ImageLoader 作用是將這些文件加載進內存,且每一個文件對應一個ImageLoader實例來負責加載。
兩步走:
在程序運行時它先將動態(tài)鏈接的 image 遞歸加載 (也就是上面測試棧中一串的遞歸調用的時刻)。
再從可執(zhí)行文件 image 遞歸加載所有符號。
動態(tài)鏈接庫加載的具體流程
1).load dylibs image 讀取庫鏡像文件
2).Rebase image
3).Bind image
4).Objc setup
5).initializers
后面會詳細介紹,這里不做介紹
總結有一點:減少非系統(tǒng)庫的依賴,減少不必要的framework,因為動態(tài)鏈接比較耗時
2,我們進行封裝時,可以封裝成靜態(tài)庫和動態(tài)庫
先介紹一下靜態(tài)庫和動態(tài)庫
動態(tài)庫形式:.dylib和.framework
靜態(tài)庫形式:.a和.framework
靜態(tài)庫:
1),首先將源文件編譯成目標文件:gcc –c a.c b.c,
2),生成靜態(tài)庫:ar –rc libstatic.a a.o b.o
當程序與靜態(tài)庫連接時,庫中目標文件所含的所有將被程序使用的函數(shù)的機器碼被copy到最終的可執(zhí)行文件中。這就會導致最終生成的可執(zhí)行代碼量相對變多,相當于編譯器將代碼補充完整了,這樣運行起來相對就快些。不過會有個缺點: 占用磁盤和內存空間. 靜態(tài)庫會被添加到和它連接的每個程序中, 而且這些程序運行時, 都會被加載到內存中. 無形中又多消耗了更多的內存空間.
官網(wǎng)文檔說明:


421ABA36-81A9-432F-86CF-D4FDE4622125.png

動態(tài)庫:

1),同靜態(tài)庫一樣編譯成目標文件:gcc –c a.c b.c

2),生成共享庫:gcc –fPIC –shared –o libshared.so a.o b.o

由此可見靜態(tài)庫和動態(tài)庫都是對目標文件的處理,也可以說庫文件已經(jīng)是機器碼文件了

與共享庫連接的可執(zhí)行文件只包含它需要的函數(shù)的引用表,而不是所有的函數(shù)代碼,只有在程序執(zhí)行時, 那些需要的函數(shù)代碼才被拷貝到內存中。這樣就使可執(zhí)行文件比較小, 節(jié)省磁盤空間,更進一步,操作系統(tǒng)使用虛擬內存,使得一份共享庫駐留在內存中被多個程序使用,也同時節(jié)約了內存。不過由于運行時要去鏈接庫會花費一定的時間,執(zhí)行速度相對會慢一些,總的來說靜態(tài)庫是犧牲了空間效率,換取了時間效率,共享庫是犧牲了時間效率換取了空間效率.

官網(wǎng)文檔說明:


E64F092B-00E1-4A72-B226-781E8C710D64.png

2,編譯分析,性能問題
上面講到了打包的原理,包括Apple提供的靜態(tài)庫和動態(tài)庫解析原理,多工程聯(lián)編無非我們關心是編譯時間,效率問題,及封裝成庫的之間相互調用效率問題
1),在編譯效率上,打包成Framework或者 static library,這樣編譯的時候這部分代碼就不需要重新編譯了,從這里可以看出封裝成framework,編譯時間跟之前是一樣,有時會更少


image.png

2),封裝成靜態(tài)庫調用效率,性能問題

image.png

而動態(tài)庫,在調用時比較耗時,上面介紹動態(tài)庫有官網(wǎng)文檔截圖
注意:Apple對待第三方開發(fā)者使用動態(tài)庫的態(tài)度卻是極端的否定,所以在iOS 7之前如果使用動態(tài)庫是肯定會被reject的,reason。但在2014年Xcode6和iOS 8發(fā)布時卻開放了這個禁地,應該主要是為了App Extension
Swift 與 Framework 的關系
在Xcode 6.0 Beta 4的 Release Notes 中,可以找到這句話:
B74B1C47-6818-448A-A177-1A1F977EBC1F.png

共享可執(zhí)行文件 iOS 有沙箱機制,不能跨App間共享共態(tài)庫,但Apple開放了App Extension,可以在App和Extension間共間動態(tài)庫(這也許是Apple開放動態(tài)鏈接庫的唯一原因了)

這個就引入必須要用到動態(tài)庫,嵌入式動態(tài)庫Embedded Framework

官網(wǎng)文檔說明:

A82AEE92-CADB-4554-8B70-07A2F3DC42B8.png

一般我們項目都是cocoapod管理第三方,cocoapod必須use_frameworks,
cocoapods 會生成相應的 .frameworks文件(動態(tài)鏈接庫:實際內容為 Header + 動態(tài)鏈接庫 + 資源文件),使用 dynamicframeworks 來取代 staticlibraries 方式
3,總結,封裝模塊,靜態(tài)庫/動態(tài)庫在編譯效率會快點,但是在相互之間調用效率沒有直接調用快,封裝成靜態(tài)庫調用,效率相差不大,由于swift項目里,必須是動態(tài)鏈接庫,所以調用效率方面會略差一點,而且封裝成庫也會引入依賴庫,也會影響效率,這是一個取舍問題,封裝成庫,便于管理,定位bug,維護起來成本也低

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容