iOS高級進階系列之-庫(下)動態庫和靜態庫項目應用

系列文章:OC底層原理系列OC基礎知識系列Swift底層探索系列iOS高級進階系列markdown

前言

前面文章講了靜態庫和動態庫,講的內容都是爲了這篇文章作準備,這邊咱們就聊一下實際SDK開發對靜態庫和動態庫的應用,平時開發也會用到文章講的內容。架構

XCFramework

XCFramework簡介

  • 1.是蘋果官方推薦的、支持的,能夠更方便的表示一個多個平臺架構分發二進制庫的格式。在19年推出
  • 2.須要Xcode11以上支持。(不是iOS系統版本,是Xcode11纔有這個功能
  • 3.是爲更好支持Mac Catalyst(是爲了將iPad上的App更好的移植到ARM芯片的macOS的一個功能)和ARM芯片的macOS。 專⻔在2019年提出的framework的另外一種先進格式

和傳統的framework相比

  • 1.能夠用單個.xcframework文件提供多個平臺的分發二進制文件;
  • 2.與Fat Header相比,能夠按照平臺劃分,能夠包含相同架構不一樣平臺的文件;
  • 3.在使用時,不須要經過腳本剝離不須要架構體系

動態庫合併

寫了一個功能名字叫SYTimer,咱們將它編譯成動態庫 image.pngpost

模擬器架構

此時須要編譯命令: image.png 點擊回車,就會發現項目開始編譯,最後在指定文件生成咱們須要文件 image.pngui

編譯過程當中一直生成.o文件,上面文件就是咱們平時Xcode編譯的文件spa

咱們展現下包內容,看下里面的東西debug

image.png

SKIP_INSTALL設置爲NO就是爲了將這個framework拷貝到這個目錄下3d

真機架構

上面咱們已經編譯了模擬器架構,下面咱們來編譯成真機架構 image.png調試

紅框就是不一樣點,一個是分發平臺,一個名字,點擊回車code

image.png

此時咱們看到文件夾中已經有兩個文件,一個是模擬器,一個是真機,下面咱們來進行合併orm

合併

在製做SDK過程當中,咱們須要提供的SDK須要即支持真機,又須要支持模擬器!此時咱們須要量兩個動態庫合併,此時咱們經常使用的就是lipo命令 image.png

上面就是lipo的解釋:建立或者操做一個文件

胖二進制

咱們先說下胖二進制胖二進制就是多個架構打包到一塊兒,可是就會有一個問題就是打包在一塊兒的架構是不能相同的,下面咱們查一下生成的真機SYTimer可執行文件的架構 image.png

咱們發現有兩個架構分別爲arm_v7,arm64

上面咱們進行打包的時候,只指定了分發平臺,並未指定架構,可是爲何會出現兩個呢?

  • 這是由於在打包的時候,會執行Xcode項目中Build settings的設置。

image.png 繼續說胖二進制,胖二進制是將各個動態庫mach-header放在一塊兒各個庫文件也是肩並肩放在一塊兒,並不是真真意義上的合併

合併

下面咱們使用lipo命令進行合併 image.png 咱們回車後發現報錯了,報錯的緣由就是兩個架構都存在arm64 image.png

這個主要是由於,咱們在進行模擬器打包時,Build Settings設置的有arm64

若是非要合併,那麼咱們須要將模擬器的x86架構提取出來,獲獎arm64架構刪除掉,下面咱們來作提取 image.png 提取出來後,咱們再作合併 image.png

此時具備多架構的SYTimer就生成了

問題:

  • 1.生成的SYTimer須要從新處理頭文件
  • 2.須要包裝成一個新的framework
  • 3.須要從新簽名

以上問題都須要手動操做 還有其餘問題,咱們在打包的時候會生成sdYM以及map image.png

dsYM用處應該都懂,而map是若是支持了bitcode,就須要BCSymbolMaps不然即便上傳dsYM也沒法解析

說完問題,怎麼解決,就引出咱們的要講的內容:蘋果在19年推出的XCFramework

使用XCFramework

下面咱們就來經過命令編譯一個XCFramework image.png 按下回車,咱們就發現生成了咱們須要的XCFramework image.png

  • 咱們能夠看到相同架構,也是能合併的,並且自動生成相應架構framework,上面咱們說的sdYM沒有放進來,咱們給它放進來

image.png

  • 注意的是-debug-symbols連接文件須要是絕對路徑不能使用相對路徑

image.png

  • 咱們上面說的sdYM和map都給連接了進來

下面咱們來使用這個xcframework

  • 1.直接將SYTimer.xcframework拖入項目

image.png

  • 2.引入頭文件,建立屬性

image.png

  • 3.選擇模擬器進行編譯

image.png 上面講了,xcframework根據不一樣平臺選擇不一樣的架構,而不像傳統的framework,都包含在內,下面咱們驗證一下

  • 1.根據模擬器編譯成功,會生成一個可執行文件,咱們查看下這個可執行文件包內容裏的Frameworks,包含的SYTimer.framework的架構

image.png image.png 這個是模擬器架構

  • 2.換成arm64架構進行編譯,查看下可執行文件

image.png image.png 這個是真機架構

總結

經過上面咱們知道xcframework比lipo命令好不少

  • 1.不用處理頭文件
  • 2.不用再去管重複的架構
  • 3.能夠把調試符號暴露出來

因此建議你們趕快將xcframework用起來吧

項目實際應用

弱引用

建立一個項目,咱們想在這個項目中引入SYTimer.framework image.png 按照咱們以前講的,連接一個framework的三要素

  • 1.指定頭文件 -I

image.png

  • 2.指定framework文件所在目錄 -F

image.png

  • 3.指定framework名稱 -l

image.png 寫完後,寫代碼運行,運行成功了 image.png 下面咱們運行項目 image.png 項目報錯,很熟悉的錯誤,這是由於咱們並無告訴framework的具體位置,咱們能夠將framework拖進項目,可是我不想這麼作,我想經過xcconfig來設置@rpath image.png 那麼這個設置的@rpath報錯的/SYTimer.framework/SYTimer拼接就能找到路徑,咱們再運行一次,運行成功 image.png

弱引用符號

以前說過弱引用,若是說這個東西在運行的時候沒有定義,它有是個弱引用符號,那麼連接器自動將它置爲null,也就是它能夠容許在運行的時候能夠爲空能夠在運行的時候找不到

  • 上面講若是沒有路徑運行會報錯

image.png

  • 咱們能夠把SYTimer.framework置爲弱引用,這樣就不會報錯了

image.png

  • 咱們看下-weak-framework連接器中的定義

image.png

  • 跟上面解釋一致,再也不過多敘述,直接運行,項目看會不會報錯

image.png 項目沒有報錯,可是打印爲null,此時咱們設置起做用的,這有什麼好處,當項目某一個庫找不到的時候也不會報錯

  • 那麼此時Mach-o作了什麼,咱們能夠看一下

image.png 此時它cmd再也不是LC_LOAD_DYLIB 而是:LC_LOAD_WEAK_DYLIB

連接衝突

咱們項目中會遇到這樣的狀況,引入第三方A,裏面有個庫叫E,在引入一個第三方B,裏面也有個庫叫E,此時就會出現衝突,由於引入了兩個E,那麼此時怎麼解決呢?下面咱們模擬一下,假設項目中引入兩個AFNetworking,咱們來連接這兩個庫 image.png 下面咱們建立xcconfig文件,在文件裏進行操做 image.png

  • 寫完後咱們編譯,沒有問題,下面咱們引入頭文件進行操做,以後編譯也沒有問題。

image.png 咱們連接了兩個相同的靜態庫,只是名字不一樣,可是在編譯期是沒有衝突的,爲何?

由於在連接靜態庫的時候,默認的是-noall_load,也就是它會進行代碼剝離,當找到AFNetworking的時候後面的AFNetworking2不會再連接進去了

  • 若是換成-all_load,再編譯

image.png

報錯,提示有223個符號衝突,那麼怎麼解決這個問題呢?

  • 咱們看下cocoapod的xcconfig文件,發現OTHER_LDFLAGS有個-ObjC,是指全部的OC代碼都會被導入,由於AFNetworking和AFNetworking2都爲OC代碼!那麼此時咱們怎麼作?咱們可使用-load_hidden

image.png

靜態庫文件能夠進行隱藏不導出任何東西

下面就是對xcconfig文件從新寫 image.png

  • 運行成功

image.png

動態庫連接動態庫

  • 1.咱們建立一個Fremework叫LJNetworkManager,而後經過cocoaPods導入動態庫AFN

image.png

  • 2.此時咱們在動態庫LJNetworkManagerLJAFNetworkingManager.m來引入動態庫AFNetworking,在LjNetworkManagerTests調用LJAFNetworkingManager方法

image.png

  • 3.此時編譯是沒有問題的,可是對LjNetworkManagerTests進行運行報錯

image.png

  • 4.緣由是並沒有找到AFNetworking,此時咱們看LjNetworkManagerTests的包內容

image.png

它是不存在Frameworks文件夾的,更不會存在AFNetworking

  • 5.如何解決呢?咱們能夠將AFN絕對路徑給到LjNetworkManagerTests

image.png

  • 6.此時再運行就會發現都成功了

image.png

但這個問題不能這麼解決,因此絕對路徑實不可取

  • 7.咱們能夠經過copy的形式將framework拷貝到包裏

image.png

這個是cocoapods中的.sh腳本中代碼,意思就是將項目拷貝到相應的目錄,這也就是爲何cocoapods管理的動態庫都沒這樣的問題,還有一種簡單的解決辦法

  • 8.讓cocoapods對LjNetworkManagerTests一樣導入AFN,而後更新

image.png

  • 9.更新後再運行,咱們就發現也成功

image.png image.png

拓展

若是我LJNetworkManager想引用LjNetworkManagerTests裏的代碼(反向依賴)是否可行,以前文章說過dylid在連接的過程當中會將全部的導出符號放在一塊兒,只要再運行的時候可以找到,就能正常運行

  • 1.暴露Header

image.png

  • 2.導入頭文件,在LJNetworkManager引入LJTestAppObject.h,就須要改一下Pods-LjNetworkManager.debug.xcconfig

image.png

  • 3.在LJAFNetworkingManager引入LJTestAppObject,發現識別出來了

image.png

  • 4.運行後發現報錯,找不到這個符號

image.png

咱們前面說過項目只要運行起來,它本身就能找到,怎麼讓項目運行起來,以前提過-undefined,這裏直接寫

image.png

此時運行就成功了,可是有個問題,咱們隨便寫的符號也會被忽略掉,不會報錯!

  • 5.指定符號-U

image.png

此時也會成功

  • 6.驗證

image.png

咱們看到此時調到LJTestAppObject方法,因此咱們上面操做是對的實現反向依賴

動態庫連接靜態庫

仍是上面的代碼,此時咱們將Podfile中引入AFN的use_frameworks!去掉 image.png

  • 1.更新Podfile後,引入的就是AFN的靜態庫

image.png

  • 2.此時編譯不會報錯

image.png

  • 3.此時咱們LjNetworkManagerTests可否引入靜態庫AFN,答案是能夠的,咱們來配置一下

image.png

  • 4.上面設置完就能夠了,由於咱們連接的是靜態庫,只須要找到頭文件就好了,以前文章說過,這裏再也不解釋。運行時成功的

image.png

拓展

當我給別人提供動態庫時,我不想靜態庫符號暴露出來,怎麼作呢?

  • 1.連接器給咱們提供了相應的參數來隱藏:-hidden-l

image.png

  • 2.此時咱們再編譯

image.png

報錯,未定義的符號!咱們經過這種方式控制靜態庫符號是否展現

靜態庫連接靜態庫

仍是上面的項目,此時咱們將咱們建立的庫變成靜態庫,而AFN一樣也是靜態庫 image.png

此時LjNetworkManagerTests使用LJAFNetworkingManager會不會報錯?

咱們分析一下:LjNetworkManagerTests引入靜態庫沒什麼問題,可是組件是個靜態庫,它引入靜態庫,須要手動集成才行,咱們編譯下: image.png

找不到符號,此時就須要咱們暴露出去

  • 1.在Build settings

image.png

  • 2.此時在編譯項目,就成功

image.png

組件連接靜態庫,並沒有暴露給LjNetworkManagerTests,因此須要咱們手動導入

靜態庫連接動態庫

仍是上面的項目,此時咱們導入AFN爲動態庫 image.png

  • LjNetworkManagerTests使用靜態庫沒什麼問題,可是要使用動態庫就須要將動態庫放入LjNetworkManagerTests中才行。編譯項目發現項目報錯

image.png

未找到符號,緣由LjNetworkManagerTests使用LJAFNetworkingManager,而LJAFNetworkingManager使用了動態庫AFN,而LjNetworkManagerTests並不知道AFN的位置,故報錯

  • 告訴LjNetworkManagerTests須要使用的AFN動態庫的位置

image.png

  • 運行項目,不成功

image.png

由於咱們的LjNetworkManagerTests不存在AFN的包,這個問題和咱們將的動態庫連接動態庫類似,這裏再也不說,你們能夠本身試試。

上面的LjNetworkManagerTests你們能夠當作一個一個新的App,我是爲了省事用LjNetworkManagerTests來代替App

拓展

Podfile導入多樣庫

上面咱們講了動態庫靜態庫間得相互連接,咱們知道咱們用cocoapods導入AFN是經過use_frameworks!來肯定導入靜態庫仍是動態庫,如今有個問題,我想在項目中即導入動態庫又導入靜態庫,該怎麼作 image.png

好比此時我想讓AFN做爲靜態庫導入,而SDWebImage做爲動態庫導入

  • 在Podfile作以下操做

image.png

簡單解釋下,就是咱們在導入podfile的第三方時,判斷若是是AFN就以靜態庫方式導入,不然就按動態庫導入

image.png

經過podfile同時導入多個項目

image.png

MulitProject.xcworkspace基於TestOC而來,而LjNetworkManager帶有Podfile的第三方庫

  • 咱們經過LjNetworkManager的Podfile同時TestOCLjNetworkManager添加依賴庫,在LjNetworkManager的Podfile作下面改動:

image.png

  • 更新下Podfile

image.png

簡單總結

XCFramework

  • 1.頭文件
  • 2.調試符號
  • 3.相同架構的處理

項目應用

  • 1.weak_import動態庫 運行時 -> 位置
  • 2.靜態庫衝突App -> all_load\ObjC
  • 3.App -> 動態庫連接動態庫 -> pod\腳本複製 -> rexport動態庫
  • 4.App -> 動態庫連接靜態庫 -> 靜態庫代碼不想暴露 -> hidden-l靜態庫
  • 5.App -> 靜態庫連接靜態庫 -> 三要素:1.名稱 2.頭文件位置 3.包所在位置
  • 6.App -> 靜態庫連接動態庫 -> 編譯報錯:不知道動態庫所在位置,運行報錯:動態庫@rpath -> pod\腳本複製

寫在最後

內容比較多,寫了一週左右時間!寫了5000多字,寫的比較細,都是把本身探索過程記錄下來,但願你們可以按着文章去實操一遍,這部分也比較無聊,可是這是高階必走的路。有什麼疑問能夠在下面留言,也但願你們多多交流,點贊!

相關文章
相關標籤/搜索