iOS之深入解析靜態(tài)庫和動態(tài)庫

      網(wǎng)友投稿 1162 2022-05-29

      一、庫

      ① 什么是庫?

      庫就是程序代碼的集合,將 N 個文件組織起來,是共享程序代碼的一種方式。

      從本質(zhì)上來說是一種可執(zhí)行代碼的二進(jìn)制格式,可以被載入內(nèi)存中執(zhí)行。

      ② 庫的分類

      根據(jù)程序代碼的開源情況,庫可以分為兩類:

      開源庫:源代碼是公開的,可以看到具體實(shí)現(xiàn),比如知名的第三方框架:AFNetworking、SDWebImage 等。

      閉源庫:不公開源代碼,只公開調(diào)用的接口,看不到具體的實(shí)現(xiàn),是一個編譯后的二進(jìn)制文件。這種常見于一些公司的 SDK 包,比如高德地圖 SDK、環(huán)信即時(shí)通訊 SDK 等。而閉源庫又分為兩類:靜態(tài)庫和動態(tài)庫。

      ③ 從源代碼到 App

      當(dāng)我們點(diǎn)擊了 build 之后,做了什么事情呢?

      預(yù)處理(Pre-process):把宏替換、刪除注釋、展開頭文件,產(chǎn)生 .i 文件。

      編譯(Compliling):把之前的 .i 文件轉(zhuǎn)換成匯編語言,產(chǎn)生 .s 文件。

      匯編(Asembly):把匯編語言文件轉(zhuǎn)換為機(jī)器碼文件,產(chǎn)生 .o 文件。

      鏈接(Link):對 .o 文件中的對于其他的庫的引用的地方進(jìn)行引用,生成最后的可執(zhí)行文件(同時(shí)也包括多個 .o 文件進(jìn)行 link)。

      ④ iOS 設(shè)備的 CPU 架構(gòu)

      模擬器:

      4s-5: i386 5s-iPhone X(包括 iPhone SE): x86_64

      1

      2

      真機(jī)(iOS 設(shè)備):

      armv6:iPhone、iPhone 2、iPhone 3G、iPod Touch(第一代)、iPod Touch(第二代) armv7:iPhone 3Gs、iPhone 4、iPhone 4s、iPad、iPad 2 armv7s:iPhone 5、iPhone 5c(靜態(tài)庫只要支持了 armv7,就可以在 armv7s 的架構(gòu)上運(yùn)行,向下兼容) arm64:iPhone 5s、iPhone 6、iPhone 6 Plus、iPhone 6s、iPhone 6s Plus、iPad Air、iPad Air2、iPad mini2、iPad mini3、iPhone 7、iPhone 7 Plus、iPhone 8、iPhone 8 Plus、iPhone X

      1

      2

      3

      4

      二、靜態(tài)庫和動態(tài)庫

      ① 什么是靜態(tài)庫和動態(tài)庫?

      靜態(tài)和動態(tài)是相對編譯期和運(yùn)行期而言的:靜態(tài)庫在程序編譯時(shí)會被鏈接到目標(biāo)代碼中,程序運(yùn)行時(shí)將不再需要該靜態(tài)庫;動態(tài)庫在程序編譯時(shí)并不會被鏈接到目標(biāo)代碼中,只是在程序運(yùn)行時(shí)才被載入。

      靜態(tài)庫在程序編譯鏈接的時(shí)候,如下圖所示:

      動態(tài)庫在程序編譯鏈接的時(shí)候,如下圖所示:

      靜態(tài)庫和動態(tài)庫都是程序編譯好的二進(jìn)制文件,蘋果的官方文檔中有兩句對動態(tài)庫和靜態(tài)庫的解釋,如下:

      動態(tài)庫:可以在運(yùn)行或者啟動的時(shí)候加載到內(nèi)存中,加載到一塊獨(dú)立的于 App 的內(nèi)存地址中;

      靜態(tài)庫:當(dāng)程序在啟動的時(shí)候,會將 App 的代碼(包括靜態(tài)庫的代碼)一起在加載到 App 所處的內(nèi)存地址上。相比于靜態(tài)庫的方案,使用動態(tài)庫將花費(fèi)更多的啟動時(shí)間和內(nèi)存消耗,還會增加可執(zhí)行文件的大小。

      A better approach is for an app to load code into its address space when it’s actually needed, either at launch time or at runtime. The type of library that provides this flexibility is called dynamic library. When an app is launched, the app’s code—which includes the code of the static libraries it was linked with—is loaded into the app’s address space.Applications with large executables suffer from slow launch times and large memory footprints.

      1

      2

      3

      ② 存在形式

      靜態(tài)庫:以 “.a” 或者 “.framework” 為文件后綴名;.a 是一個純二進(jìn)制文件,.framework 中除了有二進(jìn)制文件之外還有資源文件。.a 要有 .h 文件以及資源文件配合,.framework 文件可以直接使用。總的來說,.a + .h + sourceFile = .framework,因此創(chuàng)建靜態(tài)庫最好還是用 .framework 的形式。

      動態(tài)庫:以 “.dylib” 或者 “.framework” 為文件后綴名(Xcode7 之后 .tbd 替代 .dylib)。

      ③ 使用區(qū)別

      靜態(tài)庫鏈接時(shí)會被完整的復(fù)制到可執(zhí)行文件中,被多次使用就有多份拷貝:

      利用靜態(tài)函數(shù)庫編譯成的文件比較大,因?yàn)檎麄€函數(shù)庫的所有數(shù)據(jù)都會被整合進(jìn)目標(biāo)代碼中。它的優(yōu)點(diǎn)就顯而易見了,即編譯后的執(zhí)行程序不需要外部的函數(shù)庫支持,因?yàn)樗惺褂玫暮瘮?shù)都已經(jīng)被編譯進(jìn)去。當(dāng)然這也會成為它的缺點(diǎn),因?yàn)槿绻o態(tài)函數(shù)庫改變了,那么程序必須重新編譯。

      動態(tài)庫鏈接時(shí)不復(fù)制,程序運(yùn)行時(shí)由系統(tǒng)動態(tài)加載到內(nèi)存,供程序調(diào)用,而且系統(tǒng)只加載一次,多個程序共用,節(jié)省內(nèi)存:

      相對于靜態(tài)函數(shù)庫,動態(tài)函數(shù)庫在編譯的時(shí)候,并沒有被編譯進(jìn)目標(biāo)代碼中,程序執(zhí)行到相關(guān)函數(shù)時(shí)才調(diào)用該函數(shù)庫里的相應(yīng)函數(shù),因此動態(tài)函數(shù)庫所產(chǎn)生的可執(zhí)行文件比較小。由于函數(shù)庫沒有被整合進(jìn)程序,而是程序運(yùn)行時(shí)動態(tài)的申請并調(diào)用,所以程序的運(yùn)行環(huán)境中必須提供相應(yīng)的庫。動態(tài)函數(shù)庫的改變并不影響程序,所以動態(tài)函數(shù)庫的升級比較方便。

      ④ 各自優(yōu)點(diǎn)

      靜態(tài)庫:

      模塊化,分工合作,提高了代碼的復(fù)用及核心技術(shù)的保密程度;

      避免少量改動經(jīng)常導(dǎo)致大量的重復(fù)編譯連接;

      也可以重用,注意不是共享使用。

      動態(tài)庫:

      可以將最終可執(zhí)行文件體積縮小,將整個應(yīng)用程序分模塊,團(tuán)隊(duì)合作,進(jìn)行分工,影響比較小;

      多個應(yīng)用程序共享內(nèi)存中得同一份庫文件,節(jié)省資源;

      可以不重新編譯連接可執(zhí)行程序的前提下,更新動態(tài)庫文件達(dá)到更新應(yīng)用程序的目的;

      應(yīng)用插件化;

      軟件版本實(shí)時(shí)模塊升級;

      在其它大部分平臺上,動態(tài)庫都可以用于不同應(yīng)用間共享, 共享可執(zhí)行文件,這就大大節(jié)省了內(nèi)存。

      在 iOS8 之前,蘋果不允許第三方框架使用動態(tài)方式加載,從 iOS8 開始允許開發(fā)者有條件地創(chuàng)建和使用動態(tài)框架,這種框架叫做 Cocoa Touch Framework。雖然同樣是動態(tài)框架,但是和系統(tǒng) framework 不同,蘋果系統(tǒng)專屬的 framework 是共享的(如 UIKit),使用 Cocoa Touch Framework 制作的動態(tài)庫在打包和提交 App 時(shí)會被放到 App main bundle 的根目錄中,運(yùn)行在沙盒里,而不是系統(tǒng)中。也就是說,不同的 App 就算使用了同樣的 framework,但還是會有多份的框架被分別簽名、打包和加載。不過 iOS8 上開放了 App Extension 功能,可以為一個應(yīng)用創(chuàng)建插件,這樣主 App 和插件之間共享動態(tài)庫還是可行的。

      ⑤ 靜態(tài)庫的處理方式

      對于一個靜態(tài)庫而言,其實(shí)已經(jīng)是編譯好的類似一個 .o 的集合,這里并沒有連接。在 build 的過程中只會參與鏈接的過程,而這個鏈接的過程簡單的講就是合并,并且鏈接器只會將靜態(tài)庫中被使用的部分合并到可執(zhí)行文件中去。相比較于動態(tài)庫,靜態(tài)庫的處理起來要簡單的多,具體如下圖:

      鏈接器會將所有 .o 用到的 global symbol 和 unresolved symbol 放入一個臨時(shí)表,而且是 global symbol 是不能重復(fù)的。

      對于靜態(tài)庫的 .o,鏈接器會將沒有任何 symbol 在 unresolved symbol table 的給忽略。

      unresolved symbol 類似 extern int test(); .h 的聲明。

      global symbol 類似 void test() { print(“test”) } .m 的實(shí)現(xiàn)。

      最后,鏈接器會用函數(shù)的實(shí)際地址來代替函數(shù)引用。

      ⑥ 動態(tài)庫的處理方式

      首先,對于動態(tài)庫而言其實(shí)分動態(tài)鏈接庫和動態(tài)加載庫兩種的,這兩個最本質(zhì)的區(qū)別還是加載時(shí)間。

      動態(tài)鏈接庫:在沒有被加載到內(nèi)存的前提下,當(dāng)可執(zhí)行文件被加載,動態(tài)庫也隨著被加載到內(nèi)存中。在 Linked Framework and Libraries 設(shè)置的一些 share libraries(隨著程序啟動而啟動)。

      動態(tài)加載庫:當(dāng)需要的時(shí)候再使用 dlopen 等通過代碼或者命令的方式來加載(在程序啟動之后)。

      但是不論是哪種動態(tài)庫,相比較與靜態(tài)庫,動態(tài)庫處理起來要棘手的多。由于動態(tài)庫是動態(tài)的,所以事先不知道某個函數(shù)的具體地址,因此動態(tài)鏈接器在鏈接函數(shù)的時(shí)候需要做大量的工作。

      而實(shí)現(xiàn)這個動態(tài)鏈接是使用了 Procedure Linkage Table (PLT)。首先這個 PLT 列出了程序中每一個函數(shù)的調(diào)用,當(dāng)程序開始運(yùn)行,如果動態(tài)庫被加載到內(nèi)存中,PLT 會去尋找動態(tài)的地址并記錄下來,如果每個函數(shù)都被調(diào)用過的話,下一次調(diào)用就可以通過 PLT 直接跳轉(zhuǎn)了,但是和靜態(tài)庫還是有點(diǎn)區(qū)別的是,每一個函數(shù)的調(diào)用還是需要通過一張 PLT。這也正是所有靜態(tài)鏈接做的事情都搬到運(yùn)行時(shí)來做,會導(dǎo)致更慢的原因。

      ⑦ 動態(tài)庫的作用

      應(yīng)用插件化

      每一個功能點(diǎn)都是一個動態(tài)庫,在用戶想使用某個功能的時(shí)候讓其從網(wǎng)絡(luò)下載,然后手動加載動態(tài)庫,實(shí)現(xiàn)功能的的插件化。

      雖然技術(shù)上來說這種動態(tài)更新是可行的,但是對于 App Store 上上架的 App 是不可以的。iOS8 之后雖然可以上傳含有動態(tài)庫的 App,但是蘋果不僅需要你動態(tài)庫和 App 的簽名一致,而且蘋果會在上架的時(shí)候再經(jīng)過一次 App Store 的簽名。所以想在線更新動態(tài)庫,首先得有蘋果 App Store 私鑰,而這個基本不可能。

      除非應(yīng)用不需要通過 App Store 上架,比如企業(yè)內(nèi)部的應(yīng)用,通過企業(yè)證書發(fā)布,那么就可以實(shí)現(xiàn)應(yīng)用插件化在線更新動態(tài)庫。

      共享可執(zhí)行文件

      在其它大部分平臺上,動態(tài)庫都可以用于不同應(yīng)用間共享,這就大大節(jié)省了內(nèi)存。從目前來看,iOS 仍然不允許進(jìn)程間共享動態(tài)庫,即 iOS 上的動態(tài)庫只能是私有的,因?yàn)槲覀內(nèi)匀徊荒軐討B(tài)庫文件放置在除了自身沙盒以外的其它任何地方。

      iOS8 上開放了 App Extension 功能,可以為一個應(yīng)用創(chuàng)建插件,這樣主 App 和插件之間共享動態(tài)庫還是可行的。

      三、簽名

      系統(tǒng)在加載動態(tài)庫時(shí),會檢查 framework 的簽名,簽名中必須包含 TeamIdentifier,并且 framework 和 host app 的 TeamIdentifier 必須一致。

      在 Debug 測試的時(shí)候是不會報(bào)錯的,在打包時(shí)如果有動態(tài)庫,那么就會檢查 TeamIdentifier。如果不一致,就會報(bào)以下錯誤:

      Error loading /path/to/framework: dlopen(/path/to/framework, 265): no suitable image found. Did find:/path/to/framework: mmap() error 1

      1

      如果用來打包的證書是 iOS 8 發(fā)布之前生成的,則打出的包驗(yàn)證的時(shí)候會沒有 TeamIdentifier 這一項(xiàng)。這時(shí)在加載 framework 的時(shí)候會報(bào)以下錯誤:

      [deny-mmap] mapped file has no team identifier and is not a platform binary:/private/var/mobile/Containers/Bundle/Application/5D8FB2F7-1083-4564-94B2-0CB7DC75C9D1/YourAppNameHere.app/Frameworks/YourFramework.framework/YourFramework

      1

      可以通過 codesign 命令來驗(yàn)證:

      codesign -dv /path/to/YourApp.app // 或 codesign -dv /path/to/YourFramework.framework

      1

      2

      3

      如果證書太舊,輸出的結(jié)果如下:

      Executable=/path/to/YourApp.app/YourApp Identifier=com.company.yourapp Format=bundle with Mach-O thin (armv7) CodeDirectory v=20100 size=221748 flags=0x0(none) hashes=11079+5 location=embedded Signature size=4321 Signed Time=2019年08月09日 上午10:18:37 Info.plist entries=42 TeamIdentifier=not set Sealed Resources version=2 rules=12 files=2451 Internal requirements count=1 size=188

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      注意其中的 TeamIdentifier=not set,在用 CocoaPods 的 use_framework! 的時(shí)候生成的動態(tài)庫也可以用 codesign -dv /path/to/youFramework.framework 查看到 TeamIdentifier=not set。

      四、Framework

      ① 什么是 Framework?

      iOS之深入解析靜態(tài)庫和動態(tài)庫

      Framework 是 Cocoa/Cocoa Touch 程序中使用的一種資源打包方式,可以將代碼文件、頭文件、資源文件、說明文檔等集中在一起,方便開發(fā)者使用。一般如果是靜態(tài) Framework 的話,資源打包進(jìn) Framework 是讀取不了的。靜態(tài) Framework 和 .a 文件都是編譯進(jìn)可執(zhí)行文件里面的,只有動態(tài) Framework 能在 .app 下面的 Framework 文件夾下看到,并讀取 .framework 里的資源文件。

      Cocoa/Cocoa Touch 開發(fā)框架本身提供了大量的 Framework,比如 Foundation.framework/UIKit.framework 等。需要注意的是,這些 Framework 無一例外都是動態(tài)庫。

      平時(shí)用的第三方 SDK 的 Framework 都是靜態(tài)庫,真正的動態(tài)庫是上不了 App Store(iOS8 之后能上 AppStore,因?yàn)?App Extension,需要動態(tài)庫支持)。

      Framework 為什么既是靜態(tài)庫又是動態(tài)庫?系統(tǒng)的 .framework 是動態(tài)庫,我們自己建立的 .framework 一般都是靜態(tài)庫。但是現(xiàn)在用 Xcode 創(chuàng)建Framework 的時(shí)候默認(rèn)是動態(tài)庫,一般打包成 SDK 給別人用的話都使用的是靜態(tài)庫,可以修改 Build Settings的 Mach-O Type 為 Static Library。

      ② Framework 目錄

      Headers:表示暴露的頭文件,一般都會有一個和 Framework 同名的 .h 文件,在創(chuàng)建 Framework 的時(shí)候文件夾里也會默認(rèn)生成這樣一個文件。有這個和 Framework 同名的 .h 文件 @import 導(dǎo)入庫的時(shí)候編譯器才能找到這個庫。

      info.plist:主要就是這個 Framework 的一些配置信息。

      Modules:

      這個文件夾里有個 module.modulemap 文件:

      framework module DynamicFramework { umbrella header "DynamicFramework.h" export * module * { export * } }

      1

      2

      3

      4

      5

      6

      7

      8

      這里面有這樣一句 umbrella header “DynamicFramework.h”,umbrella 有保護(hù)傘、庇護(hù)的意思。也就是說 Headers 中暴露的 DynamicFramework.h 文件被放在 umbrella 雨傘下保護(hù)起來,所以需要將其他的所有需要暴露的 .h 文件放到 DynamicFramework.h 文件中保護(hù)起來,不然會出現(xiàn)警告。@import 的時(shí)候也只能找到 umbrella 雨傘下保護(hù)起來的 .h 文件。

      二進(jìn)制文件(Unix 可執(zhí)行文件):這個就是源碼編譯而成的二進(jìn)制文件,主要的執(zhí)行代碼就在這個里面。

      .bundle 文件:如果在 Build Phases -> Copy Bundle Resources 里加入 .bundle 文件,那么創(chuàng)建出來的 .Framework 里就會有這個 .bundle 的資源文件夾。

      ③ Framework 的資源文件

      CocoaPods 如何生成 Framework 的資源文件?我們能看到用 CocoaPods 創(chuàng)建 Framework 的時(shí)候,F(xiàn)ramework 里面有一個 .bundle 文件,跟 Framework 同級目錄里也有一個 .bundle文件,這兩個文件其實(shí)是一樣的。

      那這兩個 .bundle 是怎么來的呢?我們能看到用 use_frameworks! 生成的 pod 里面,pods 這個 PROJECT 下面會為每一個 pod 生成一個 target。

      那么如果這個 pod 有資源文件的話,就會有一個叫 xxx-bundleName 的 target,最后這個 target 生成的就是 bundleName.bundle。

      在 xxx 的 target 的 Build Phases -> Copy Bundle Resources 里加入這個 .bundle,在 Framework 里面就會生成這樣一個 bundle。在 xxx 的 target 的 Build Phases -> Target Dependencies 里加入這個 target:xxx-bundleName,就會在 Framework 的同級目錄里生成這樣一個 bundle。

      靜態(tài) Framework 里不需要加入資源文件,一般資源打包進(jìn)靜態(tài) Framework 是讀取不了的。靜態(tài) Framework 和 .a 文件都是編譯進(jìn)可執(zhí)行文件里面的,只有動態(tài) Framework 能在 .app 的 Framework 文件夾下看到,并讀取 .framework 里的資源文件。

      可以用 NSBundle * bundle = [[NSBundle mainBundle] bundlePath]; 得到 .app 目錄,如果是動態(tài)庫能在 Framework 目錄下看到這個動態(tài)庫以及動態(tài)庫里面資源文件,然后只要用 NSBundle * bundle = [NSBundle bundleForClass:<#ClassFromFramework#>]; 得到這個動態(tài)庫的路徑就能讀取到里面的資源了。但是如果是靜態(tài)庫的話,因?yàn)榫幾g進(jìn)了可執(zhí)行文件里面,也就沒辦法讀到這個靜態(tài)庫了,能看到 .app 下的 Framework 目錄為空。

      五、動態(tài)庫和靜態(tài)庫的支持和兼容

      ① Swift 支持

      跟著 iOS8/Xcode6 同時(shí)發(fā)布的還有 Swift。如果要在項(xiàng)目中使用外部的代碼,可選的方式只有兩種:把代碼拷貝到工程中和用動態(tài) Framework,使用靜態(tài)庫是不支持的。

      造成這個問題的原因主要是 Swift 的運(yùn)行庫沒有被包含在 iOS 系統(tǒng)中,而是會打包進(jìn) App 中(這也是造成 Swift App 體積大的原因),靜態(tài)庫會導(dǎo)致最終的目標(biāo)程序中包含重復(fù)的運(yùn)行庫(這是蘋果自家的解釋)。同時(shí)拷貝 Runtime 這種做法也會導(dǎo)致在純 ObjC 的項(xiàng)目中使用 Swift 庫出現(xiàn)問題。蘋果聲稱等到 Swift 的 Runtime 穩(wěn)定之后會被加入到系統(tǒng)當(dāng)中,到時(shí)候這個限制就會被去除。

      ② CocoaPods 的做法

      在純 ObjC 的項(xiàng)目中,CocoaPods 使用編譯靜態(tài)庫 .a 方法將代碼集成到項(xiàng)目中。在 Pods 項(xiàng)目中的每個 target 都對應(yīng)著一個 Pod 的靜態(tài)庫:

      當(dāng)不想發(fā)布代碼的時(shí)候,也可以使用 Framework 發(fā)布 Pod,CocoaPods 提供 vendored_framework 選項(xiàng)來使用第三方 Framework。

      對于 Swift 項(xiàng)目,CocoaPods 提供了動態(tài) Framework 的支持,通過 use_frameworks! 選項(xiàng)控制。對于 Swift 寫的庫來說,想通過 CocoaPods 引入工程,必須加入 use_frameworks! 選項(xiàng)。

      ③ 關(guān)于 use_frameworks!

      在使用 CocoaPods 的時(shí)候在 Podfile 里加入 use_frameworks!,那么在編譯的時(shí)候就會默認(rèn)生成動態(tài)庫,我們能看到每個源碼 Pod 都會在 Pods 工程下面生成一個對應(yīng)的動態(tài)庫 Framework 的 target,能在這個 target 的 Build Settings -> Mach-O Type 看到默認(rèn)設(shè)置是 Dynamic Library,也就是會生成一個動態(tài) Framework,能在 Products 下面看到每一個 Pod 對應(yīng)生成的動態(tài)庫。

      這些生成的動態(tài)庫將鏈接到主項(xiàng)目給主工程使用,但是我們上面說過動態(tài)庫需要在主工程 target 的 General -> Embedded Binaries 中添加才能使用,而我們并沒有在 Embedded Binaries 中看到這些動態(tài)庫,那這是怎么回事呢?其實(shí)是 CocoaPods 已經(jīng)執(zhí)行了腳本把這些動態(tài)庫嵌入到了 .app 的 Framework 目錄下,相當(dāng)于在 Embedded Binaries 加入了這些動態(tài)庫,我們能在主工程 target 的 Build Phase -> Embed Pods Frameworks 里看到執(zhí)行的腳本。

      所以 Pod 默認(rèn)是生成動態(tài)庫,然后嵌入到 .app 下面的 Framework 文件夾里,我們?nèi)?Pods 工程的 target 里把 Build Settings -> Mach-O Type 設(shè)置為 Static Library。那么生成的就是靜態(tài)庫,但是 CocoaPods 也會把它嵌入到 .app 的 Framework 目錄下,而因?yàn)樗庆o態(tài)庫,所以會報(bào)錯:unrecognized selector sent to instanceunrecognized selector sent to instance。

      六、常見問題

      如果靜態(tài)庫中有 category 類,則在使用靜態(tài)庫的項(xiàng)目配置中【Other Linker Flags】需要添加參數(shù)[-ObjC] 或者 [-all_load]。

      出現(xiàn) Umbrella header for module ‘XXXX’ does not include header ‘XXXXX.h’,因?yàn)榘?xxxxx.h 錯誤的拖到了 public 中。

      出現(xiàn) dyld: Library not loaded:XXXXXX,是因?yàn)榇虬?Framework 版本太高。比如打包 Framework 時(shí),選擇的是 iOS 9.0,而實(shí)際的工程環(huán)境是 iOS 8 開始的,需要到 iOS Deployment Target 設(shè)置對應(yīng)版本。

      報(bào)錯 “Include of non-modular header inside framework module”,如果創(chuàng)建的 Framework 類中使用了 .dylib 或者 .tbd,首先需要在實(shí)際項(xiàng)目中導(dǎo)入 .dylib 或者 .tbd 動態(tài)庫,然后需要設(shè)置 Allow Non-modular Includes In Framework Modules = YES。

      有時(shí)候會發(fā)現(xiàn)在使用的時(shí)候加載不了動態(tài) Framework 里的資源文件,其實(shí)是加載方式不對,比如用 pod 的時(shí)候使用的是 use_frameworks!,那么資源是在 Framework 里面的,需要使用以下代碼加載:

      NSBundle *bundle = [NSBundle bundleForClass:<#ClassFromFramework#>]; NSString *path = [bundle pathForResource:@"imageName@2x"(@"bundleName.bundle/imageName@2x") ofType:@"png"]; UIImage *image = [UIImage imageWithContentsOfFile:path];

      1

      2

      3

      4

      報(bào)錯 Reason: image not found:如果直接在工程里使用創(chuàng)建的動態(tài)庫時(shí)候會出現(xiàn)此錯誤,需要在工程的 General 里的 Embedded Binaries 添加這個動態(tài)庫才能使用。因?yàn)閯?chuàng)建的這個動態(tài)庫其實(shí)也不能給其他程序使用的,而 App Extension 和 APP 之間是需要使用這個動態(tài)庫的,這個動態(tài)庫可以 App Extension 和 APP 之間共用一份(App 和 Extension 的 Bundle 是共享的),因此蘋果又把這種 Framework 稱為 Embedded Framework。

      iOS 匯編語言

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:為什么我們應(yīng)該使用 Flutter?
      下一篇:關(guān)于開源許可證的介紹
      相關(guān)文章
      亚洲国产高清在线一区二区三区| 亚洲福利视频网站| 亚洲成a人片在线观看播放| 亚洲AV无码国产丝袜在线观看 | 国产偷国产偷亚洲高清在线| 亚洲精品无码aⅴ中文字幕蜜桃| 亚洲综合中文字幕无线码| 亚洲三级视频在线观看| 亚洲a级片在线观看| 亚洲sss综合天堂久久久| 亚洲人成网男女大片在线播放| 亚洲人成日本在线观看| 国产成人精品亚洲2020| 亚洲综合激情五月色一区| 亚洲精品无码日韩国产不卡av| 亚洲av永久无码| 精品亚洲成A人在线观看青青| 亚洲?V乱码久久精品蜜桃 | 亚洲av永久无码| 亚洲福利精品一区二区三区| 亚洲一区二区三区免费| 国产亚洲美日韩AV中文字幕无码成人 | 久久亚洲2019中文字幕| 美腿丝袜亚洲综合| 国产亚洲av片在线观看16女人 | 亚洲免费在线视频观看| 亚洲AV男人的天堂在线观看| 亚洲日韩AV一区二区三区四区 | 国产亚洲综合久久| 国产午夜亚洲精品理论片不卡| 亚洲乱码国产一区三区| 亚洲AV永久纯肉无码精品动漫| 亚洲黄色网址大全| 亚洲精品人成网在线播放影院| 亚洲av中文无码乱人伦在线观看| 亚洲国产激情一区二区三区| 亚洲综合精品香蕉久久网| 亚洲国产精品久久久久久| 亚洲伦理一二三四| 久久精品国产亚洲AV| 日韩精品亚洲aⅴ在线影院|