系列文章:OC底層原理系列,OC基礎知識系列,Swift底層探索系列,iOS高級進階系列markdown
前面文章講了靜態庫和動態庫
,講的內容都是爲了這篇文章作準備,這邊咱們就聊一下實際SDK開發
中對靜態庫和動態庫的應用
,平時開發也會用到文章講的內容。架構
一個多個平臺
和架構
的分發二進制庫的格式
。在19年推出
Xcode11以上支持
。(不是iOS系統版本
,是Xcode11纔有這個功能
)更好
的支持Mac Catalyst
(是爲了將iPad上的App更好的移植到ARM芯片的macOS的一個功能)和ARM芯片的macOS
。 專⻔在2019年提出的framework的另外一種先進格式
。單個.xcframework文件提供多個平臺的分發二進制文件
;Fat Header
相比,能夠按照平臺劃分
,能夠包含相同架構
的不一樣平臺
的文件;不須要
再經過腳本
去剝離不須要
的架構體系
。寫了一個功能名字叫SYTimer
,咱們將它編譯成動態庫
post
此時須要編譯命令: 點擊回車,就會發現項目開始編譯,最後在指定文件生成
咱們須要
的文件
ui
編譯過程當中
一直生成.o文件
,上面文件就是咱們平時Xcode編譯的文件spa
咱們展現下包內容,看下里面的東西debug
SKIP_INSTALL設置爲NO就是爲了將這個framework拷貝到這個目錄下3d
上面咱們已經編譯了模擬器架構
,下面咱們來編譯成真機架構
調試
紅框就是不一樣點,一個是
分發平臺
,一個名字,點擊回車code
此時咱們看到文件夾中已經有兩個文件,一個是
模擬器
,一個是真機
,下面咱們來進行合併
orm
在製做SDK過程當中,咱們須要提供的SDK須要即支持真機
,又須要支持模擬器
!此時咱們須要量兩個動態庫合併
,此時咱們經常使用的就是lipo命令
上面就是lipo的解釋:
建立或者操做一個文件
。
咱們先說下胖二進制
,胖二進制
就是多個架構打包到一塊兒
,可是就會有一個問題就是打包在一塊兒的架構是不能相同
的,下面咱們查一下生成的真機SYTimer可執行文件的架構
咱們發現有兩個架構分別爲
arm_v7,arm64
!
上面咱們進行打包的時候,只指定了分發平臺,並未指定架構,可是爲何會出現兩個呢?
繼續說胖二進制,胖二進制是將各個動態庫
的mach-header放在一塊兒
,各個庫
的文件
也是肩並肩放在一塊兒
,並不是真真意義上的合併
。
下面咱們使用lipo命令進行合併
咱們回車後發現報錯了,報錯的緣由就是兩個架構都存在arm64
這個主要是由於,咱們在進行模擬器打包時,
Build Settings設置的有arm64
若是非要合併,那麼咱們須要將模擬器的x86架構提取出來,獲獎arm64架構刪除掉,下面咱們來作提取 提取出來後,咱們再作合併
此時具備多架構的SYTimer就生成了
問題:
須要從新處理頭文件
包裝成一個新的framework
從新簽名
以上問題都須要手動操做 還有其餘問題,咱們在打包的時候會生成sdYM以及map
dsYM用處應該都懂,而
map
是若是支持了bitcode
,就須要BCSymbolMaps
,不然
即便上傳dsYM也沒法解析
說完問題,怎麼解決,就引出咱們的要講的內容:蘋果在19年推出的XCFramework
下面咱們就來經過命令編譯一個XCFramework
按下回車,咱們就發現生成
了咱們須要的XCFramework
相同架構
,也是能合併
的,並且自動生成相應架構
的framework
,上面咱們說的sdYM沒有放進來
,咱們給它放進來-debug-symbols連接文件
須要是絕對路徑
,不能
使用相對路徑
sdYM和map都給連接了進來
下面咱們來使用這個xcframework
上面講了,xcframework
會根據不一樣
的平臺選擇不一樣的架構
,而不像傳統的framework,都包含在內,下面咱們驗證一下
模擬器編譯成功
,會生成一個可執行文件,咱們查看下這個可執行文件包內容裏的Frameworks,包含的SYTimer.framework的架構 這個是模擬器架構
arm64架構
進行編譯
,查看下可執行文件
這個是真機架構
經過上面咱們知道xcframework比lipo命令好不少
不用處理頭文件
不用再去管重複的架構
能夠把調試符號暴露出來
因此建議你們趕快將xcframework用起來吧
建立一個項目,咱們想在這個項目中引入SYTimer.framework 按照咱們以前講的,連接一個framework的三要素
指定頭文件 -I
指定framework文件所在目錄 -F
指定framework名稱 -l
寫完後,寫代碼運行,運行成功了 下面咱們運行項目 項目報錯,很熟悉的錯誤,這是由於咱們並無告訴framework的具體位置
,咱們能夠將framework拖進項目
,可是我不想這麼作,我想經過xcconfig來設置@rpath
那麼這個設置的@rpath
和報錯的/SYTimer.framework/SYTimer拼接
就能找到路徑,咱們再運行一次,運行成功
以前說過弱引用
,若是說這個東西在運行
的時候沒有定義
,它有是個弱引用
的符號
,那麼連接器
自動將它置爲null
,也就是它能夠容許在運行的時候能夠爲空
,能夠在運行的時候找不到
沒有路徑運行會報錯
把SYTimer.framework置爲弱引用
,這樣就不會報錯了-weak-framework
在連接器中的定義
項目沒有報錯
,可是打印爲null
,此時咱們設置起做用
的,這有什麼好處,當項目某一個庫找不到的時候也不會報錯
此時它cmd再也不是
:LC_LOAD_DYLIB
而是:LC_LOAD_WEAK_DYLIB
咱們項目中會遇到這樣的狀況,引入第三方A
,裏面有個庫叫E
,在引入一個第三方B
,裏面也有個庫叫E
,此時就會出現衝突
,由於引入了兩個E
,那麼此時怎麼解決呢?下面咱們模擬一下,假設項目中引入兩個AFNetworking,咱們來連接這兩個庫 下面咱們建立xcconfig文件,在文件裏進行操做
咱們連接了兩個相同的靜態庫
,只是名字不一樣
,可是在編譯期是沒有衝突
的,爲何?
由於在連接靜態庫的時候,默認的是
-noall_load
,也就是它會進行代碼剝離
,當找到AFNetworking
的時候後面的AFNetworking2
就不會再連接
進去了
-all_load
,再編譯報錯,提示有
223個符號衝突
,那麼怎麼解決這個問題呢?
cocoapod的xcconfig文件
,發現OTHER_LDFLAGS有個-ObjC
,是指全部的OC代碼都
會被導入,由於AFNetworking和AFNetworking2都爲OC代碼!那麼此時咱們怎麼作?咱們可使用-load_hidden
對
靜態庫文件
能夠進行隱藏
,不導出任何東西
下面就是對xcconfig文件從新寫
LJNetworkManager
,而後經過cocoaPods導入動態庫AFN
LJNetworkManager
的LJAFNetworkingManager.m
來引入動態庫AFNetworking
,在LjNetworkManagerTests
,調用LJAFNetworkingManager方法
編譯是沒有問題
的,可是對LjNetworkManagerTests
進行運行
時報錯
的沒有找到AFNetworking
,此時咱們看LjNetworkManagerTests的包內容
它是
不存在Frameworks文件夾
的,更不會存在AFNetworking
將AFN
的絕對路徑給到LjNetworkManagerTests
但這個問題不能這麼解決,因此
絕對路徑實不可取
的
copy
的形式將framework拷貝到包裏
這個是
cocoapods
中的.sh腳本中代碼
,意思就是將項目拷貝到相應的目錄
,這也就是爲何cocoapods管理的動態庫都沒這樣的問題,還有一種簡單的解決辦法
讓cocoapods對LjNetworkManagerTests一樣導入AFN
,而後更新更新
後再運行
,咱們就發現也成功
了
若是我LJNetworkManager
想引用LjNetworkManagerTests
裏的代碼(反向依賴
)是否可行,以前文章說過dylid在連接的過程當中
會將全部的導出符號放在一塊兒
,只要再運行的時候可以找到,就能正常運行
LJNetworkManager引入LJTestAppObject.h
,就須要改一下Pods-LjNetworkManager.debug.xcconfig
LJAFNetworkingManager
中引入LJTestAppObject
,發現識別出來了咱們前面說過項目
只要運行起來
,它本身就能找到,怎麼讓項目運行起來,以前提過-undefined
,這裏直接寫
此時運行就成功了,可是有個問題,咱們
隨便寫的符號
也會被忽略掉
,不會報錯!
指定符號-U
此時也會成功
咱們看到此時
調到
了LJTestAppObject方法
,因此咱們上面操做是對的
,實現
了反向依賴
仍是上面的代碼,此時咱們將Podfile
中引入AFN的use_frameworks!去掉
AFN的靜態庫
編譯不會報錯
LjNetworkManagerTests
可否引入靜態庫AFN
,答案是能夠的,咱們來配置一下連接的是靜態庫
,只須要找到頭文件
就好了,以前文章說過,這裏再也不解釋。運行時成功的當我給別人提供動態庫時
,我不想靜態庫
的符號暴露出來
,怎麼作呢?
連接器
給咱們提供了相應的參數來隱藏:-hidden-l
報錯,
未定義的符號
!咱們經過這種方式
來控制靜態庫符號是否展現
仍是上面的項目,此時咱們將咱們建立的庫變成靜態庫
,而AFN一樣也是靜態庫
此時LjNetworkManagerTests使用LJAFNetworkingManager會不會報錯?
咱們分析一下:LjNetworkManagerTests引入靜態庫沒什麼問題
,可是組件是個靜態庫
,它引入靜態庫
,須要手動集成
才行,咱們編譯下:
找不到符號
,此時就須要咱們暴露出去
編譯項目
,就成功
了
組件連接靜態庫
,並沒有暴露
給LjNetworkManagerTests,因此須要咱們手動導入
仍是上面的項目,此時咱們導入AFN爲動態庫
使用靜態庫
,沒什麼問題
,可是要使用動態庫
就須要將動態庫放入LjNetworkManagerTests中
才行。編譯項目發現項目報錯未找到符號,緣由
LjNetworkManagerTests使用LJAFNetworkingManager
,而LJAFNetworkingManager使用了動態庫AFN
,而LjNetworkManagerTests並不知道AFN的位置
,故報錯
AFN動態庫的位置
由於咱們的
LjNetworkManagerTests不存在AFN的包
,這個問題和咱們將的動態庫連接動態庫類似
,這裏再也不說,你們能夠本身試試。
上面的LjNetworkManagerTests你們能夠當作一個一個新的App,我是爲了省事用LjNetworkManagerTests來代替App
上面咱們講了動態庫
和靜態庫
間得相互連接
,咱們知道咱們用cocoapods導入AFN是經過use_frameworks
!來肯定導入靜態庫仍是動態庫
,如今有個問題,我想在項目中即導入動態庫又導入靜態庫
,該怎麼作
好比此時我想讓
AFN做爲靜態庫導入
,而SDWebImage做爲動態庫導入
簡單解釋下,就是咱們在
導入podfile的第三方時
,判斷若是是AFN
就以靜態庫方式導入
,不然就按動態庫導入
MulitProject.xcworkspace
是基於TestOC而來
,而LjNetworkManager
是帶有Podfile的第三方庫
Podfile
去同時
給TestOC
和LjNetworkManager添加依賴庫
,在LjNetworkManager的Podfile作下面改動:頭文件
調試符號
相同架構的處理
weak_import
:動態庫
運行時
-> 位置
靜態庫衝突
:App
-> all_load\ObjC
動態庫連接動態庫
-> pod\腳本複製
-> rexport動態庫
動態庫連接靜態庫
-> 靜態庫代碼不想暴露
-> hidden-l靜態庫
靜態庫連接靜態庫
-> 三要素:1.名稱 2.頭文件位置 3.包所在位置
靜態庫連接動態庫
-> 編譯報錯:不知道動態庫所在位置
,運行報錯:動態庫@rpath -> pod\腳本複製
內容比較多,寫了一週左右時間!寫了5000多字,寫的比較細,都是把本身探索過程記錄下來
,但願你們可以按着文章去實操一遍
,這部分也比較無聊,可是這是高階必走的路。有什麼疑問能夠在下面留言,也但願你們多多交流,點贊!