系列文章:OC底層原理系列,OC基礎知識系列,Swift底層探索系列,iOS高級進階系列shell
以前的文章iOS高級進階系列之-庫(上)靜態庫探索在後面講到了Dead_Strip
,也說道了-all_load
、-ObjC
以及-force_load
,這篇文章讓你們以爲-noall_load
、-all_load
、-ObjC
以及-force_load
能夠控制Dead_Strip
,其實不是這樣的markdown
Build Settings
查找Dead經過上面能夠看出來這個
Dead_Strip
和-noall_load
、-all_load
、-ObjC
以及-force_load沒有太大關係
,-noall_load、-all_load、-ObjC以及-force_load
只是在鏈接靜態庫
的時候起做用
,而Dead_Strip
只是給開發者提供了一種優化代碼的方式
架構
上圖是test.m的代碼,下圖是文件,經過腳本將test.m編譯成可執行文件。前面的文章介紹過:先將
test.m編譯成靜態庫test.o文件
,再將TestExample.m編譯成靜態庫TestExample.o
,最後經過將test.o鏈接TestExample.o生成可執行文件
。咱們直接執行腳本函數
當生成可執行文件後,查看macho
信息 咱們能夠發現這個裏面沒有任何關於TestExample
的內容
,下面咱們對腳本進行改進 post
腳本
默認
的爲-noall_load
,它不會將沒有用過
的代碼放入到靜態庫
中優化
改完腳本再次執行腳本 ui
發現多了不少東西,並且
未使用的TestExample也導入到項目中
了spa
咱們看下dead_strip的定義 3d
大意就是
刪除沒有被入口點
或者導出符號使用過的代碼
。咱們再改一下腳本code
運行下腳本,看下變化
這裏看的是符號表,咱們看下以前代碼
咱們發現
global_function
是全局導出符號
,可是未被使用
,可是在導出的符號表
中並沒有global_function
,這就是-dead_strip做用
下面咱們調用下global_function方法 在執行腳本,讀取符號表
發現
global_function在符號表
中
沒有被入口點使用就會被幹掉
沒有被導出符號使用的也會被幹掉
下面咱們將TestExample代碼放開,在執行腳本 再來看下符號表
發現都在符號表裏
下面該下腳本和代碼 問題:此時符號表仍是否有TestExample內容
發現仍是
存在
的,緣由是OC代碼
是動態運行的代碼
,因此不敢幹掉
,權限不夠
shell命令有個能夠查看某個代碼爲何存在的指令:-why_live
查看
global_function爲何會存在
解釋爲何global_function符號存在,
它被從test.o來的,它被main函數使用,main函數又從test.o來
上面講完後對Dead_Strip和-noall_load、-all_load、-ObjC以及-force_load沒有太大關係有了更好的理解了吧
首先咱們來把test.m和AFNetworking動態庫進行鏈接
下圖爲test.m代碼,咱們引入了AFN,並初始化AFHTTPSessionManager
test.m
編譯成目標文件
靜態庫中介紹過命令的用法,這裏再也不說明
鏈接動態庫
和鏈接靜態庫
使用的指令
都是同樣
的
運行test可執行文件
當咱們運行,當即
發生了錯誤
那究竟爲何會出錯了?咱們下面探究一下
準備以前的代碼
紅框的
-dynamiclib
就是告訴編譯器
我下面要執行
的是編譯動態庫
錯誤
chmod +x ./build.sh
給權限
再次運行腳本
咱們發現仍是報和上面相同的錯誤,爲何呢,是否是腳本命令是否是有問題?
靜態庫的文章已經說過靜態庫
是.o的合集
(文章直接演示將.o文件直接改爲靜態庫能夠運行),可是動態庫
是一個連接編譯
的最終產物
。這也就意味着靜態庫
能夠經過連接變成動態庫
報錯了,就是在連接的過程當中
找不到_TestExample導出符號
動態庫的符號表
發現
導出符號表
是空的
,什麼都沒有
前面介紹
編譯期
在鏈接
的時候默認執行
的是-noall_load
,由於動態庫
並沒有使用
到.a
的內容
,因此不加載
。那麼是否是這個緣由形成的呢?咱們改下腳本
添加-all_load
發現
符號表有內容
了
仍是熟悉的味道,是否是有點無語,問題究竟出在哪裏呢?下面就來介紹動態庫的另外一個特性
靜態庫是.o文件的合集
動態庫是.o文件連接事後的產物
靜態庫能夠經過連接生成動態庫
動態庫也就是最終產物,這也是動態庫沒法合併緣由
下面咱們連連接下動態庫,咱們下面來說解LoginApp
怎麼去連接動態庫SYCSSColor動態庫
咱們再看看SYCSSColor裏面的內容
xcconfig
文件,具體內容查看iOS高級進階系列之-項目開發基礎(上)多環境配置,Mach-O與連接器這個是連接路徑
SYCSSColor.tbd文件
直接拖到項目
中,不在xcconfig中寫編譯連接
,看看會發生什麼經過上面咱們看到了,咱們
引入
了我SYCSSColor
的頭文件
,使用了代碼而且編譯成功
了
咱們只是將SYCSSColor.tbd
文件放入項目
中,那麼什麼是tbd格式呢?
text-based stub libraries
,本質上就是一個YAML描述的文本文件
。記錄動態庫
的一些信息
,包括導出的符號
、動態庫的架構信息
、動態庫的依賴信息
避免
在真機開發過程當中直接使用傳統的dylib
。動態庫
都是在設備上
,在Xcode上使用基於tbd格式
的僞framework
能夠大大減小Xcode的大小
。咱們查看下SYCSSColor.tbd的內容,
exports導出
的意思symbols是符號
的意思,也就意味着11-14行都是導出符號
objc-classes是objc類的集合
經過上面咱們能夠知道咱們用腳本去連接庫的是用到-L,-l連接
的是符號
,也就是咱們只須要知道符號所在的位置
,不須要知道源碼位置
。上面編譯經過了,下面就運行看看 上面看到直接崩潰,爲何呢?這是由於動態庫是動態連接
的,在運行
的時候,由dyld動態加載動態庫
,在加載
的時候,它回去找這個符號真實地址
的時候,找不到
了,而靜態庫
在編譯的時候就已將符號歸
放在一塊兒,不會動態加載
Xcode的Build Settings
搜索text
原理:就是經過
拼上一些參數
,來掃描Headers裏面的頭文件
,而後把這個符號寫到文件
裏去
動態庫是.dyld的形式
,其實是沒有什麼標識的,下面咱們吧生成的刪除,重新生成一份叫TestExample
將test.m
和咱們的Frameworks進行連接
這就說明咱們前面製做的
.framework
是成功
的
仍是相同錯誤,爲何還會有這樣的錯誤,這就要從dyld加載動態庫
提及。以下圖:
Mach-o
至關於咱們的可執行文件Test
Mach-o
有個專門的LC_LOAD_DYLIB,它裏面有咱們
須要用到的動態庫路徑`找不到
這個路徑的時候就會報錯
下面咱們看下路徑究竟是什麼東西
咱們發現咱們Test中一共使用了5個動態庫,那麼使用哪5個呢?
-A 5 意思就是找到動態庫後
多現實5行
咱們看到咱們使用的動態庫中name字段就是該動態庫路徑,可是咱們導入的動態庫TestExample路徑
只有一個名字
,別的什麼都沒有,這樣確定找不到TestExample。 下面咱們就來解決這個問題
動態庫
有一個專門的地方
來保存本身的路徑
,也就是說動態庫的路徑是保存
在本身的Mach-o中
的,下面咱們就來查看下這個路徑
-A是向下展現
,向上展現時-B
咱們發現這個地方的路徑就沒有給對,我要給對正確的路徑,外部有個命令來更改路徑名稱:install_name_tool
咱們看到
install_name_tool
給的解釋就是改變更態庫
的install name
下面咱們就使用一下,來改變路徑
發現報錯,這是由於咱們並
沒有告訴編譯期
往哪裏加這個路徑
發現此時的路徑已經更改了,那咱們再來執行一次build.sh腳本,從新生成test文件,在查看下引入的動態庫
咱們發現此時的路徑已經改變了,那麼此時咱們再運行可執行文件
此時並無報錯,成功運行了起來。也說明咱們此時將test.m成功的鏈接了TestExample動態庫
上面咱們講了是生成動態庫後去更改路徑的,那麼能夠在生成前進行更改呢?答案是固然能夠,咱們在腳本中更改,咱們使用命令-install_name
,先看下install_name解釋
就是添加路徑
【問題】上面咱們看到給到的是一個絕對路徑,絕對路徑有一個很大的問題,當咱們作好SDK後給別人使用,那麼這個路徑就會改變,別人就須要更改路徑,走着沒法調用成功
上面的問題怎麼解決,這就須要雙方約定好規則,什麼規則,看下圖:
A使用B,B提供一個
TestExample路徑
(Frameworks/TestExample.framework/TestExample),而A給B提供一個使用者路徑(也就是test的路徑)而後B給拼接起來,此時路徑不久完整了嘛!
這就牽扯到@rpath
,@rpath:Runpath search Paths! dyld搜索路徑
。運行時@rpath
指示dyld按順序搜索路徑列表
,以找到動態庫
。@rpath
保存一個或多個路徑的變量! 也就是誰鏈接我誰給我提供@rpath
,下面咱們直接修改路徑
此時咱們看到路徑已經修改完了
下面就是須要test給TestExample提供@rpath
了,怎麼提供,可執行文件test的mach-o中存一個@rpath
,在鏈接的時候,將其提供給TestExample。
此時咱們在test可執行文件中
搜索rpath
,發現不存在
,說明此時若是咱們去連接TestExample是找不到位置的,此時須要咱們手動添加
咱們查找一下命令
上面是
替換rpath
,下面的是添加rpath到指定的Mach-o
這時候就完成了,此時咱們從新運行可執行文件
成功了
咱們上面看到咱們打印的path
,依然是一個絕對路徑
,那麼此時有麼有什麼辦法,改成相對路徑呢?這裏系統給咱們提供了兩個方法:
@executable_path
:表示可執行程序所在的目錄
,解析爲可執行文件的絕對路徑。@loader_path
:表示被加載的Mach-O所在的目錄
,每次加載時,均可能被設置爲不一樣的路徑,由上層指定。也就是說若是用了@executable_path
,無論在誰的電腦上它都指向執行程序所在的路徑 下面咱們來執行如下,由於咱們已經設置過了rpath,因此須要替換
,上面已經鎖了替換方法,來進行操做 此時咱們再看下test的rpath路徑 已經將絕對路徑修改爲@executable_path
,運行一下test文件,驗證一下 咱們看到運行成功,說明咱們的修改是正確的
咱們看下這種狀況,test中使用的framework中包含TestExample.framework
,而TestExample.framework的framework中包含TestExampleLog.framework
下面咱們編譯一遍,從後往前編譯
TestExampleLog.framework
TestExample.framework
咱們發現連接的TestExampleLog.framework的name是不正確的,並且自身的name一樣不正確
此時編譯完成,但此時運行時報錯的,由於路徑不對
,下面咱們來改一下
TestExampleLog.framework
中的腳本
加入name
,地址使用rpath
,再運行腳本
此時的路徑就對了
TestExample.framework
的腳本運行腳本
test是提供rpath路徑
的 運行腳本
咱們發現報錯了,由於
找不到TestExampleLog的路徑
,也就是咱們在TestExample.framework
的腳本中配了本身的路徑
,可是並無配TestExampleLog的路徑
TestExample.framework的腳本
(截取部分)運行腳本 下面咱們手動看一下
咱們看到這個路徑比較長,下面咱們再來生成下test,若是上面寫的都是對的,那就能運行起來
咱們看到這樣運行時運行起來了,因此說咱們以前配置的路徑是對的
可是有個問題,咱們上面的路徑太長了,下面就用到了@loader_path
,上面講了那麼多,一方面爲了給你們屬性下腳本,第二是讓你們更好的理解。
在運行腳本,執行可執行文件
爲了加深@executable_path印象
,咱們能夠看看cocoaPods的xcconfig
文件
此時的
name
是一個名稱
當咱們編譯代碼,看下可執行文件的包內容,咱們發現動態庫
都會存放在framework文件
中,也就是上面設置的文件裏
到此最難的連接動態庫的一種方式已經說完了,連接動態庫
因爲動態加載
,因此必定要指定好路徑
,否咋就會報錯。
install_name_tool
來更改路徑
@rpath
-rpath lod new 是替換路徑
,-add_rpath是將路徑添加到指定位置
@executable_path
和@loader_path
上面講了能夠替換路徑
,那麼就想到了破解
,由於咱們能夠改變路徑
,將咱們本身寫的動態庫
做爲目標軟件的依賴
,達到破解的目的
。咱們這麼作事改變了Mach-o
,以前講過Mach-o只有簽名
後纔會被蘋果系統認可
,因此再破解軟件是都會調用一句命令:code sign --force --deep --sign -
這句命令也就是強制打一個簽名
當咱們作動態庫時,能夠在Xcode中設置咱們上面說的命令 咱們在搜下rpath
咱們上面的路徑設置均可以在Xcode去處理
咱們上面說的test
是能夠使用TestExample的方法
,那麼他test
能夠使用TestExampleLog的代碼
嗎? 咱們看下導出符號 咱們看到這裏面沒有TestExampleLog的導出符號
,因此不能使用
,那麼怎麼纔可以使用呢?在以前在講符號的時候,說過從新導出符號
,下面說下命令:reexport
從新導出framework
從新導出TestExampleLog
,運行腳本
多了一個
從新導出符號TestExampleLog
,此時就可使用TestExampleLog代碼
了
咱們更改的代碼以及打印都能正常運行
,驗證了咱們上面改的東西
動態庫寫的內容比較多,寫了一週多時間!寫了將近6000字,寫的比較細,都是把本身探索過程記錄下來
,但願你們可以按着文章去實操一遍
,這部分也比較無聊,可是這是高階必走的路,在過程當中學會shell語言
,會寫腳本
,這部分都是基礎
,後面會介紹項目高階應用:靜態庫合併,動態庫優化
等內容。有什麼疑問能夠在下面留言,也但願你們多多交流,點贊!