iOS 靜態庫,動態庫與 Framework 淺析

靜態庫與動態庫的區別

首先來看什麼是庫,庫(Library)說白了就是一段編譯好的二進制代碼,加上頭文件就能夠供別人使用。html

何時咱們會用到庫呢?一種狀況是某些代碼須要給別人使用,可是咱們不但願別人看到源碼,就須要以庫的形式進行封裝,只暴露出頭文件。另一種狀況是,對於某些不會進行大的改動的代碼,咱們想減小編譯的時間,就能夠把它打包成庫,由於庫是已經編譯好的二進制了,編譯的時候只須要 Link 一下,不會浪費編譯時間。react

上面提到庫在使用的時候須要 Link,Link 的方式有兩種,靜態和動態,因而便產生了靜態庫和動態庫。linux

靜態庫

靜態庫即靜態連接庫(Windows 下的 .lib,Linux 和 Mac 下的 .a)。之因此叫作靜態,是由於靜態庫在編譯的時候會被直接拷貝一份,複製到目標程序裏,這段代碼在目標程序裏就不會再改變了。ios

靜態庫的好處很明顯,編譯完成以後,庫文件實際上就沒有做用了。目標程序沒有外部依賴,直接就能夠運行。固然其缺點也很明顯,就是會使用目標程序的體積增大。git

動態庫

動態庫即動態連接庫(Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib/.tbd)。與靜態庫相反,動態庫在編譯時並不會被拷貝到目標程序中,目標程序中只會存儲指向動態庫的引用。等到程序運行時,動態庫纔會被真正加載進來。github

動態庫的優勢是,不須要拷貝到目標程序中,不會影響目標程序的體積,並且同一份庫能夠被多個程序使用(由於這個緣由,動態庫也被稱做共享庫)。同時,編譯時才載入的特性,也可讓咱們隨時對庫進行替換,而不須要從新編譯代碼。動態庫帶來的問題主要是,動態載入會帶來一部分性能損失,使用動態庫也會使得程序依賴於外部環境。若是環境缺乏動態庫或者庫的版本不正確,就會致使程序沒法運行(Linux 下喜聞樂見的 lib not found 錯誤)。編程

iOS Framework

除了上面提到的 .a 和 .dylib/.tbd 以外,Mac OS/iOS 平臺還可使用 Framework。Framework 其實是一種打包方式,將庫的二進制文件,頭文件和有關的資源文件打包到一塊兒,方便管理和分發。json

在 iOS 8 以前,iOS 平臺不支持使用動態 Framework,開發者可使用的 Framework 只有蘋果自家的 UIKit.Framework,Foundation.Framework 等。這種限制多是出於安全的考慮(見這裏的討論)。換一個角度講,由於 iOS 應用都是運行在沙盒當中,不一樣的程序之間不能共享代碼,同時動態下載代碼又是被蘋果明令禁止的,沒辦法發揮出動態庫的優點,實際上動態庫也就沒有存在的必要了。bootstrap

因爲上面提到的限制,開發者想要在 iOS 平臺共享代碼,惟一的選擇就是打包成靜態庫 .a 文件,同時附上頭文件(例如微信的SDK)。可是這樣的打包方式不夠方便,使用時也比較麻煩,你們仍是但願共享代碼都能能像 Framework 同樣,直接扔到工程裏就能夠用。因而人們想出了各類奇技淫巧去讓 Xcode Build 出 iOS 可使用的 Framework,具體作法參考這裏這裏,這種方法產生的 Framework 還有 「僞」(Fake) Framework 和 「真」(Real) Framework 的區別。swift

iOS 8/Xcode 6 推出以後,iOS 平臺添加了動態庫的支持,同時 Xcode 6 也原生自帶了 Framework 支持(動態和靜態均可以),上面提到的的奇技淫巧也就沒有必要了(新的作法參考這裏)。爲何 iOS 8 要添加動態庫的支持?惟一的理由大概就是 Extension 的出現。Extension 和 App 是兩個分開的可執行文件,同時須要共享代碼,這種狀況下動態庫的支持就是必不可少的了。可是這種動態 Framework 和系統的 UIKit.Framework 仍是有很大區別。系統的 Framework 不須要拷貝到目標程序中,咱們本身作出來的 Framework 哪怕是動態的,最後也仍是要拷貝到 App 中(App 和 Extension 的 Bundle 是共享的),所以蘋果又把這種 Framework 稱爲 Embedded Framework

Swift 支持

跟着 iOS8 / Xcode 6 同時發佈的還有 Swift。若是要在項目中使用外部的代碼,可選的方式只有兩種,一種是把代碼拷貝到工程中,另外一種是用動態 Framework。使用靜態庫是不支持的。

形成這個問題的緣由主要是 Swift 的運行庫沒有被包含在 iOS 系統中,而是會打包進 App 中(這也是形成 Swift App 體積大的緣由),靜態庫會致使最終的目標程序中包含重複的運行庫(這是蘋果自家的解釋)。同時拷貝 Runtime 這種作法也會致使在純 ObjC 的項目中使用 Swift 庫出現問題。蘋果聲稱等到 Swift 的 Runtime 穩定以後會被加入到系統當中,到時候這個限制就會被去除了(參考這個問題 的問題描述,也是來自蘋果自家文檔)。

CocoaPods 的作法

在純 ObjC 的項目中,CocoaPods 使用編譯靜態庫 .a 方法將代碼集成到項目中。在 Pods 項目中的每一個 target 都對應這一個 Pod 的靜態庫。不過在編譯過程當中並不會真的產出 .a 文件。若是須要 .a 文件的話,能夠參考這裏,或者使用 CocoasPods-Packager這個插件。

當不想發佈代碼的時候,也可使用 Framework 發佈 Pod,CocoaPods 提供了vendored_framework 選項來使用第三方 Framework,具體的作法能夠參考這裏這裏

對於 Swift 項目,CocoaPods 提供了動態 Framework 的支持,經過 use_frameworks!選項控制。

更多有關代碼分發的擴展資料能夠參考這篇博客: http://geeklu.com/2014/02/objc-lib/

參考資料

 

 

 

 

 

使用靜態庫的好處

1,模塊化,分工合做

2,避免少許改動常常致使大量的重複編譯鏈接

3,也能夠重用,注意不是共享使用

動態庫使用有以下好處:

1使用動態庫,能夠將最終可執行文件體積縮小

2使用動態庫,多個應用程序共享內存中得同一份庫文件,節省資源

3使用動態庫,能夠不從新編譯鏈接可執行程序的前提下,更新動態庫文件達到更新應用程序的目的。

從1能夠得出,將整個應用程序分模塊,團隊合做,進行分工,影響比較小。

等其餘好處,

從2能夠看出,其實動態庫應該叫共享庫,那麼從這個意義上來講,蘋果禁止iOS開發中使用動態庫就能夠理解了:

由於在如今的iPhone,iPodTouch,iPad上面程序都是單進程的,也就是某一時刻只有一個進程在運行,那麼你寫個共享庫,

----共享給誰?(你使用的時候只有你一個應用程序存在,其餘的應該被掛起了,即使是能夠同時多個進程運行,別人能使用你的共享庫裏的東西嗎?你這個是給你本身的程序定製的。)

----目前蘋果的AppStore不支持模塊更新,沒法更新某個單獨文件(除非本身寫一個更新機制:有本身的服務端放置最新動態庫文件)

至於蘋果爲啥禁止ios開發使用動態庫我就猜到上面倆緣由

深刻理解iPhone靜態庫

在實際的編程過程當中,一般會把一些公用函數製成函數庫,供其它程序使用,一則提搞了代碼的複用;二則提搞了核心技術的保密程度。因此在實際的項目開發中,常常會使用到函數庫,函數庫分爲靜態庫和動態庫兩種。和多數人所熟悉的動態語言和靜態語言同樣,這裏的所謂靜態和動態是相對編譯期和運行期的:靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將再也不須要改靜態庫;而動態庫在程序編譯時並不會被連接到目標代碼中,只是在程序運行時才被載入,由於在程序運行期間還須要動態庫的存在。

iPhone官方只支持靜態庫聯編。

深刻理解framework(框架,其實至關於靜態框架,不是動態庫)

打包framework仍是一個比較重要的功能,能夠用來作一下事情:

(1)封裝功能模塊,好比有比較成熟的功能模塊封裝成一個包,而後之後本身或其餘同事用起來比較方便。

(2)封裝項目,有時候會遇到這個狀況,就是一家公司找了兩個開發公司作兩個項目,而後要求他們的項目中的一個嵌套進另外一個項目,此時也能夠把唄嵌套的項目打包成framework放進去,這樣比較方便。

咱們爲何須要框架(Framework)?

要想用一種開發者友好的方式共享庫是很麻煩的。你不只僅須要包含庫自己,還要加入全部的頭文件,資源等等。

蘋果解決這個問題的方式是框架(framework)。基本上,這是含有固定結構幷包含了引用該庫時所必需的全部東西的文件夾。不幸的是,iOS禁止全部的動態庫。同時,蘋果也從Xcode中移除了建立靜態iOS框架的功能。

Xcode仍然能夠支持建立框架的功能,重啓這個功能,咱們須要對Xcode作一些小小的改動。

把代碼封裝在靜態框架是被app store所容許的。儘管形式不一樣,本質上它仍然是一種靜態庫。

框架(Framework)的類別

大部分框架都是動態連接庫的形式。由於只有蘋果才能在iOS設備上安裝動態庫,因此咱們沒法建立這種類型的框架。

靜態連接庫和動態庫同樣,只不過它是在編譯時連接二進制代碼,所以使用靜態庫不會有動態庫那樣的問題(即除了蘋果誰也不能在iOS上使用動態庫)。

「僞」框架是經過破解Xcode的目標Bundle(使用某些腳本)來實現的。它在表面上以及使用時跟靜態框架並沒有區別。「僞」框架項目的功能幾乎和真實的框架項目沒有區別(不是所有)。

「嵌入」框架是靜態框架的一個包裝,以便Xcode能獲取框架內的資源(圖片、plist、nib等)。

本次發佈包括了建立靜態框架和「僞」框架的模板,以及兩者的「嵌入」框架。

用哪種模板?

本次發佈有兩個模板,每一個模板都有「強」「弱」兩個類別。你能夠選擇最適合一種(或者兩種都安裝上)。

最大的不一樣是Xcode不能建立「真」框架,除非你安裝靜態框架文件xcspec在Xcode中。這真是一個遺憾(這個文件是給項目使用的,而不是框架要用的)。

簡單第

簡單說,你能夠這樣決定用哪種模板:

若是你不想修改Xcode,那麼請使用「僞」框架版本

若是你只是想共享二進制(不是項目),兩種均可以

若是你想把框架共享給不想修改Xcode的開發者,使用「僞」框架版本

若是你想把框架共享給修改過Xcode的開發者,使用「真」框架版本

若是你想把框架項目做爲另外一個項目的依賴(經過workspace或者子項目的方式),請使用「真」框架(或者「僞」框架,使用-framework——見後)

若是你想在你的框架項目中加入其餘靜態庫/框架,並把它們也連接到最終結果以便不須要單獨添加到用戶項目中,使用「僞」框架

「僞」框架

「僞」框架是破解的「reloacatable object file」(可重定位格式的目標文件, 保存着代碼和數據,適合於和其餘的目標文件鏈接到一塊兒,用來建立一個可執行目標文件或者是一個可共享目標文件),它可讓Xcode編譯出相似框架的東西——其實也是一個bundle。

「僞框架」模板把整個過程分爲幾個步驟,用某些腳本去產生一個真正的靜態框架(基於靜態庫而不是reloacatable object file)。並且,框架項目仍是把它定義爲wrapper.cfbundle類型,一種Xcode中的「二等公民」。

所以它跟「真」靜態框架同樣能夠正常工做,但當存在依賴關係時就有麻煩了。

依賴問題

若是不使用依賴,只是建立普通的項目是沒有任何問題的。可是若是使用了項目依賴(好比在workspace中),Xcode就悲劇了。當你點擊「Link Binary With Libraries」下方的’+’按鈕時,「僞框架」沒法顯示在列表中。你能夠從你的「僞」框架項目的Products下面將它手動拖入,但當你編輯你的主項目時,會出現警告:

warning: skipping file '/somewhere/MyFramework.framework' (unexpectedfile type 'wrapper.cfbundle' in Frameworks & Libraries build phase)

並伴隨「僞」框架中的連接錯誤。

幸運的是,有個辦法來解決它。你能夠在」Other Linker Flags」中用」-framwork」開關手動告訴linker去使用你的框架進行連接:

-framework MyFramework

警告仍然存在,但起碼能正確連接了。

添加其餘的庫/框架

若是你加入其餘靜態(不是動態)庫/框架到你的「僞」框架項目中,它們將「連接」進你最終的二進制框架文件中。在「真」框架項目中,它們是純引用,而不是連接。

你能夠在項目中僅僅包含頭文件而不是靜態庫/框架自己的方式避免這種狀況(以便編譯經過)。

「真」框架

「真」框架各個方面都符合「真」的標準。它是真正的靜態框架,正如使用蘋果在從Xcode中去除的那個功能所建立的同樣。

爲了能建立真正的靜態框架項目,你必需在Xcode中安裝一個xcspec文件。

若是你發佈一個「真」框架項目(而不是編譯),但願去編譯這個框架的人必需也安裝xcspec文件(使用本次發佈的安裝腳本),以便Xcode能理解目標類型。

注意:若是你正在發佈徹底編譯的框架,而不是框架項目,最終用戶並不須要安裝任何東西。

我已經提交一個報告給蘋果,但願他們在Xcode中更新這個文件,但那須要一點時間.OpenRadarlink here

加其餘靜態庫/框架

若是你加入其餘靜態(不是動態)庫/框架到你的「真」框架項目,它們只會被引用,而不會象「僞」框架同樣被連接到最終的二進制文件中。

從早期版本升級

若是你是從Mk6或者更早的版本升級,同時使用「真」靜態框架,而且使用Xcode4.2.1之前的版本,請運行uninstall_legacy.sh以卸載早期用於Xcode的全部修正。而後再運行install.sh,重啓Xcode。若是你使用Xcode4.3之後,只須要運行install.sh並重啓Xcode。

安裝

分別運行Real Framework目錄或Fake Framework目錄下的install.sh腳本進行安裝(或者兩個你都運行)。

重啓Xcode,你將在新項目嚮導的Framework&Library下看到StaticiOS Framework(或者Fake Static iOS Framework)。

卸載請運行unistall.sh腳本並重啓Xcode。

建立一個iOS框架項目

建立新項目。

項目類型選擇Framework&Library下的Static iOS Framework(或者Fake Static iOS Framework)。

選擇「包含單元測試」(可選的)。

在target中加入類、資源等。

凡是其餘項目要使用的頭文件,必需聲明爲public。進入target的Build Phases頁,展開Copy Headers項,把須要public的頭文件從Project或Private部分拖拽到Public部分。

編譯你的 iOS 框架

選擇指定target的scheme

修改scheme的Run配置(可選)。Run配置默認使用Debug,但在準備部署的時候你可能想使用Release。

編譯框架(不管目標爲iOS device和Simulator都會編譯出相同的二進制,所以選誰都無所謂了)。

從Products下選中你的framework,「show in Finder」。

在build目錄下有兩個文件夾:(yourframework).frameworkand(your framework).embeddedframework.

若是你的框架只有代碼,沒有資源(好比圖片、腳本、xib、coredata的momd文件等),你能夠把(yourframework).framework分發給你的用戶就好了。若是還包含有資源,你必需分發(your framework).embeddedframework給你的用戶。

爲何須要embedded framework?由於Xcode不會查找靜態框架中的資源,若是你分發(your framework).framework, 則框架中的全部資源都不會顯示,也不可用。

一個embedded framework只是一個framework以外的附加的包,包括了這個框架的全部資源的符號連接。這樣作的目的是讓Xcode可以找到這些資源。

使用iOS 框架

iOS框架和常規的Mac OS動態框架差很少,只是它是靜態連接的而已。

在你的項目中使用一個框架,只需把它拖僅你的項目中。在包含頭文件時,記住使用尖括號而不是雙引號括住框架名稱。例如,對於框架MyFramework:

#import

使用問題

Headers Not Found

若是Xcode找不到框架的頭文件,你多是忘記將它們聲明爲public了。參考「建立一個iOS框架項目」第5步。

No Such Product Type

若是你沒有安裝iOS Universal Framework在Xcode,並企圖編譯一個universal框架項目(對於「真」框架,不是「假」框架),這會致使下列錯誤:

target specifies product type 'com.apple.product-type.framework.static',but there's no such product type for the 'iphonesimulator' platform

爲了編譯「真」iOS靜態框架,Xcode須要作一些改動,所以爲了編譯「真」靜態框架項目,請在全部的開發環境中安裝它(對於使用框架的用戶不須要,只有要編譯框架才須要)。

The selected run destination is not valid for this action

有時,Xcode出錯並加載了錯誤的active設置。首先,請嘗試重啓Xcode。若是錯誤繼續存在,Xcode產生了一個壞的項目(由於Xcode4的一個bug,任何類型的項目都會出現這個問題)。若是是這樣,你須要建立一個新項目重來一遍。

連接警告

第一次編譯框架target時,Xcdoe會在連接階段報告找不到文件夾:

ld: warning: directory not found for option'-L/Users/myself/Library/Developer/Xcode/DerivedData/MyFramework-ccahfoccjqiognaqraesrxdyqcne/Build/Products/Debug-iphoneos'

此時,能夠clean並從新編譯target,警告會消除。

Core Data momd not found

對於框架項目和應用程序項目,Xcode會以不一樣的方式編譯momd(託管對象模型文件)。Xcode會簡單地在根目錄建立.mom文件,而不會建立一個.momd目錄(目錄中包含VersionInfo.plist和.mom文件)。

這意味着,當從一個embedded framework的model中實例化NSManagedObjectModel時,你必需使用.mom擴展名做爲model的URL,而不是採用.momd擴展名。

NSURL *modelURL = [[NSBundle mainBundle]URLForResource:@"MyModel" withExtension:@"mom"];

Unknown class MyClass in Interface Builder file.

因爲靜態框架採用靜態連接,linker會剔除全部它認爲無用的代碼。不幸的是,linker不會檢查xib文件,所以若是類是在xib中引用,而沒有在O-C代碼中引用,linker將從最終的可執行文件中刪除類。這是linker的問題,不是框架的問題(當你編譯一個靜態庫時也會發生這個問題)。蘋果內置框架不會發生這個問題,由於他們是運行時動態加載的,存在於iOS設備固件中的動態庫是不可能被刪除的。

有兩個解決的辦法:

讓框架的最終用戶關閉linker的優化選項,經過在他們的項目的Other Linker Flags中添加-ObjC和-all_load。

在框架的另外一個類中加一個該類的代碼引用。例如,假設你有個MyTextField類,被linker剔除了。假設你還有一個MyViewController,它在xib中使用了MyTextField,MyViewController並無被剔除。你應該這樣作:

在MyTextField中:

+ (void)forceLinkerLoad_ {}

在MyViewController中:

+(void) initialize {     [MyTextField forceLinkerLoad_]; }

他們仍然須要添加-ObjC到linker設置,但不須要強制all_load了。

第2種方法須要你多作一點工做,但卻讓最終用戶避免在使用你的框架時關閉linker優化(關閉linker優化會致使object文件膨脹)。

unexpected file type 'wrapper.cfbundle' in Frameworks &Libraries build phase

這個問題發生在把「假」框架項目做爲workspace的依賴,或者把它看成子項目時(「真」框架項目沒有這個問題)。儘管這種框架項目產生了正確的靜態框架,但Xcode只能從項目文件中看出這是一個bundle,所以它在檢查依賴性時發出一個警告,並在linker階段跳過它。

你能夠手動添加一個命令讓linker在連接階段能正確連接。在依賴你的靜態框架的項目的OtherLinker Flags中加入:

-framework MyFramework

警告仍然存在, 但不會致使連接失敗。

Libraries being linked or not being linked into the finalframework

很不幸, 「真」框架和「假」框架模板在處理引入的靜態庫/框架的工做方式不一樣的。

「真」框架模板採用正常的靜態庫生成步驟,不會連接其餘靜態庫/框架到最終生產物中。

「假」框架模板採用「欺騙」Xcode的手段,讓它認爲是在編譯一個可重定位格式的目標文件,在連接階段就如同編譯一個可執行文件,把全部的靜態代碼文件連接到最終生成物中(儘管不會檢查是否確實目標代碼)。爲了實現象「真」框架同樣的效果,你能夠只包含庫/框架的頭文件到你的項目中,而不須要包含庫/框架自己。

Unrecognized selector in (some class with a category method)

若是你的靜態庫或靜態框架包含了一個模塊(只在類別代碼中聲明,沒有類實現),linker會搞不清楚,並把代碼從二進制文件中剔除。由於在最終生成的文件中沒有這個方法,因此當調用這個類別中定義的方法時,會報一個「unrecognizedselector」異常。

要解決這個,在包含這個類別的模塊代碼中加一個「假的」類。linker發現存在完整的O-C類,會將類別代碼連接到模塊。

我寫了一個頭文件 LoadableCategory.h,以減輕這個工做量:

#import "SomeConcreteClass+MyAdditions.h"

#import "LoadableCategory.h"  MAKE_CATEGORIES_LOADABLE(SomeConcreteClass_MyAdditions);   @implementation SomeConcreteClass(MyAdditions)

...

@end

在使用這個框架時,仍然還須要在Build Setting的Other Linker Flags中加入-ObjC。

執行任何代碼前單元測試崩潰

若是你在Xcode4.3中建立靜態框架(或庫)target時,勾選了「withunit tests」,當你試圖運行單元測試時,它會崩潰:

Thread 1: EXC_BAD_ACCESS (code=2, address=0x0) 0 0x00000000 --- 15 dyldbootstrap:start(...)

這是lldb中的一個bug。你能夠用GDB來運行單元測試。編輯scheme,選擇Test,在Info標籤中將調試器Debugger從LLDB改成GDB。

 

原文連接:http://www.jianshu.com/p/4666ce7dc622
 
 
 
 
 
 
 

前言

1.靜態庫和動態庫有什麼異同?

靜態庫:連接時完整地拷貝至可執行文件中,被屢次使用就有多份冗餘拷貝。利用靜態函數庫編譯成的文件比較大,由於整個 函數庫的全部數據都會被整合進目標代碼中,他的優勢就顯而易見了,即編譯後的執行程序不須要外部的函數庫支持,由於全部使用的函數都已經被編譯進去了。固然這也會成爲他的缺點,由於若是靜態函數庫改變了,那麼你的程序必須從新編譯。

動態庫:連接時不復制,程序運行時由系統動態加載到內存,供程序調用,系統只加載一次,多個程序共用,節省內存。因爲函數庫沒有被整合進你的程序,而是程序運行時動態的申請並調用,因此程序的運行環境中必須提供相應的庫。動態函數庫的改變並不影響你的程序,因此動態函數庫的升級比較方便。

靜態庫和動態庫都是閉源庫,只能拿來知足某個功能的使用,不會暴露內部具體的代碼信息,而從github上下載的第三方庫大可能是開源庫

靜態庫和動態庫都是由*.o目標文件生成

使用靜態庫的好處

  • 模塊化,分工合做
  • 避免少許改動常常致使大量的重複編譯鏈接
  • 也能夠重用,注意不是共享使用

動態庫使用有以下好處:

  • 能夠將最終可執行文件體積縮小
  • 多個應用程序共享內存中得同一份庫文件,節省資源
  • 能夠不從新編譯鏈接可執行程序的前提下,更新動態庫文件達到更新應用程序的目的。

將整個應用程序分模塊,團隊合做,進行分工,影響比較小。

其實動態庫應該叫共享庫,那麼從這個意義上來講,蘋果禁止iOS開發中使用動態庫就能夠理解了: 由於在如今的iPhone,iPodTouch,iPad上面程序都是單進程的,也就是某一時刻只有一個進程在運行,那麼你寫個共享庫

----共享給誰?(你使用的時候只有你一個應用程序存在,其餘的應該被掛起了,即使是能夠同時多個進程運行,別人能使用你的共享庫裏的東西嗎?你這個是給你本身的程序定製的。)
    ----目前蘋果的AppStore不支持模塊更新,沒法更新某個單獨文件(除非本身寫一個更新機制:有本身的服務端放置最新動態庫文件)

至於蘋果爲啥禁止ios開發使用動態庫我就猜到上面倆緣由

2.這兩種庫都有哪些文件格式?

靜態庫:.a和.framework (windows:.lib , linux: .a)

動態庫:.dylib和.framework(系統提供給咱們的framework都是動態庫!)(windows:.dll , linux: .so)

注意:二者都有framework的格式,可是當你建立一個framework文件時,系統默認是動態庫的格式,若是想作成靜態庫,須要在buildSetting中將Mach-O Type選項設置爲Static Library就好了!

3..a文件和.framework文件的區別?

.a是一個純二進制文件,不能直接拿來使用,須要配合頭文件、資源文件一塊兒使用。

將靜態庫打包的時候,只能打包代碼資源,圖片、本地json文件和xib等資源文件沒法打包進去,使用.a靜態庫的時候須要三個組成部分:.a文件+須要暴露的頭文件+資源文件;

.framework中除了有二進制文件以外還有資源文件,能夠拿來直接使用。

4.製做靜態庫須要注意的幾點:

  • 注意理解:不管是.a靜態庫還.framework靜態庫,咱們須要的都是二進制文件+.h+其它資源文件的形式,不一樣的是,.a自己就是二進制文件,須要咱們本身配上.h和其它文件才能使用,而.framework自己已經包含了.h和其它文件,能夠直接使用。
  • 圖片資源的處理:兩種靜態庫,通常都是把圖片文件單獨的放在一個.bundle文件中,通常.bundle的名字和.a或.framework的名字相同。.bundle文件很好弄,新建一個文件夾,把它更名爲.bundle就能夠了,右鍵,顯示包內容能夠向其中添加圖片資源。
  • category是咱們實際開發項目中常常用到的,把category打成靜態庫是沒有問題的,可是在用這個靜態庫的工程中,調用category中的方法時會有找不到該方法的運行時錯誤(selector not recognized),解決辦法是:在使用靜態庫的工程中配置other linkerflags的值爲-ObjC。
  • 若是一個靜態庫很複雜,須要暴露的.h比較多的話,就能夠在靜態庫的內部建立一個.h文件(通常這個.h文件的名字和靜態庫的名字相同),而後把全部須要暴露出來的.h文件都集中放在這個.h文件中,而那些本來須要暴露的.h都不須要再暴露了,只須要把.h暴露出來就能夠了。

5.framework動態庫的主要做用:

framework原本是蘋果專屬的內部提供的動態庫文件格式,可是自從2014年WWDC以後,開發者也能夠自定義建立framework實現動態更新(繞過apple store審覈,從服務器發佈更新版本)的功能,這與蘋果限定的上架的app必須通過apple store的審覈制度是衝突的,因此含有自定義的framework的app是沒法在商店上架的,可是若是開發的是企業內部應用,就能夠考慮嘗試使用動態更新技術來將多個獨立的app或者功能模塊集成在一個app上面!(筆者開發的就是企業內部使用的app,咱們將企業官網中的板塊開發成4個獨立的app,而後將其改造爲framework文件集成在一款平臺級的app當中進行使用)

目前 iOS 上的動態更新方案主要有如下 4 種:

  • HTML 5
  • lua(wax)hotpatch
  • react native
  • framework

前面三種都是經過在應用內搭建一個運行環境來實現動態更新(HTML 5 是原生支持),在用戶體驗、與系統交互上有必定的限制,對開發者的要求也更高(至少得熟悉 lua 或者 js)。

使用 framework 的方式來更新能夠不依賴第三方庫,使用原生的 OC/Swift 來開發,體驗更好,開發成本也更低。

因爲 Apple 不但願開發者繞過 App Store 來更新 app,所以只有對於不須要上架的應用,才能以 framework 的方式實現 app 的更新。

主要思路

將 app 中的某個模塊(好比一個 tab)的內容獨立成一個 framework 的形式動態加載,在 app 的 main bundle 中,當 app 啓動時從服務器上下載新版本的 framework 並加載便可達到動態更新的目的。

實戰

建立一個普通工程 DynamicUpdateDemo,其包含一個 framework 子工程 Module。也能夠將 Module 建立爲獨立的工程,建立工程的過程再也不贅述。

依賴

在主工程的 Build Phases > Target Dependencies 中添加 Module,而且添加一個 New Copy Files Phase。

這樣,打包時會將生成的 Module.framework 添加到 main bundle 的根目錄下。

加載

主要的代碼以下:

- (UIViewController *)loadFrameworkNamed:(NSString *)bundleName {
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDirectory = nil;
    if ([paths count] != 0) {
        documentDirectory = [paths objectAtIndex:0];
    }

    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *bundlePath = [documentDirectory stringByAppendingPathComponent:[bundleName stringByAppendingString:@".framework"]];

    // Check if new bundle exists
    if (![manager fileExistsAtPath:bundlePath]) {
        NSLog(@"No framework update");
        bundlePath = [[NSBundle mainBundle]
                      pathForResource:bundleName ofType:@"framework"];

        // Check if default bundle exists
        if (![manager fileExistsAtPath:bundlePath]) {
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Oooops" message:@"Framework not found" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [alertView show];
            return nil;
        }
    }

    // Load bundle
    NSError *error = nil;
    NSBundle *frameworkBundle = [NSBundle bundleWithPath:bundlePath];
    if (frameworkBundle && [frameworkBundle loadAndReturnError:&error]) {
        NSLog(@"Load framework successfully");
    }else {
        NSLog(@"Failed to load framework with err: %@",error);
        return nil;
    }

    // Load class
    Class PublicAPIClass = NSClassFromString(@"PublicAPI");
    if (!PublicAPIClass) {
        NSLog(@"Unable to load class");
        return nil;
    }

    NSObject *publicAPIObject = [PublicAPIClass new];
    return [publicAPIObject performSelector:@selector(mainViewController)];
}

代碼先嚐試在 Document 目錄下尋找更新後的 framework,若是沒有找到,再在 main bundle 中尋找默認的 framework。 其中的關鍵是利用 OC 的動態特性 NSClassFromString 和 performSelector 加載 framework 的類而且執行其方法。

framework 和 host 工程資源共用

第方三庫

Class XXX is implemented in both XXX and XXX. One of the two will be used. Which one is undefined.

這是當 framework 工程和 host 工程連接了相同的第三方庫或者類形成的。

爲了讓打出的 framework 中不包含 host 工程中已包含的三方庫(如 cocoapods 工程編譯出的 .a 文件),能夠這樣:

  • 刪除 Build Phases > Link Binary With Libraries 中的內容(若有)。此時編譯會提示三方庫中包含的符號找不到。

  • 在 framework 的 Build Settings > Other Linker Flags 添加 -undefined dynamic_lookup。必須保證 host 工程編譯出的二進制文件中包含這些符號。

類文件

嘗試過在 framework 中引用 host 工程中已有的文件,經過 Build Settings > Header Search Paths 中添加相應的目錄,Xcode 在編譯的時候能夠成功(由於添加了 -undefined dynamic_lookup),而且 Debug 版本是能夠正常運行的,可是 Release 版本動態加載時會提示找不到符號:

Error Domain=NSCocoaErrorDomain Code=3588 "The bundle 「YourFramework」 couldn’t be loaded." (dlopen(/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework, 265): Symbol not found: _OBJC_CLASS_$_BorderedView
      Referenced from: /var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework
      Expected in: flat namespace
     in /var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework) UserInfo=0x174276900 {NSLocalizedFailureReason=The bundle couldn’t be loaded., NSLocalizedRecoverySuggestion=Try reinstalling the bundle., NSFilePath=/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework, NSDebugDescription=dlopen(/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework, 265): Symbol not found: _OBJC_CLASS_$_BorderedView
      Referenced from: /var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework
      Expected in: flat namespace
     in /var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework, NSBundlePath=/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework, NSLocalizedDescription=The bundle 「YourFramework」 couldn’t be loaded.}

由於 Debug 版本暴露了全部自定義類的符號以便於調試,所以你的 framework 能夠找到相應的符號,而 Release 版本則不會。

目前能想到的方法只有將相同的文件拷貝一份到 framework 工程裏,而且更改類名。

訪問 framework 中的圖片

在 storyboard/xib 中能夠直接訪問圖片,代碼中訪問的方法以下:

UIImage *image = [UIImage imageNamed:@"YourFramework.framework/imageName"]

注意:使用代碼方式訪問的圖片不能夠放在 xcassets 中,不然獲得的將是 nil。而且文件名必須以 @2x/@3x 結尾,大小寫敏感。由於 imageNamed: 默認在 main bundle 中查找圖片。

常見錯誤

Architecture

dlopen(/path/to/framework, 9): no suitable image found.  Did find:
/path/to/framework: mach-o, but wrong architecture

這是說 framework 不支持當前機器的架構。 經過

lipo -info /path/to/MyFramework.framework/MyFramework

能夠查看 framework 支持的 CPU 架構。

碰到這種錯誤,通常是由於編譯 framework 的時候,scheme 選擇的是模擬器,應該選擇iOS Device。

此外,若是沒有選擇iOS Device,編譯完成後,Products 目錄下的 .framework 文件名會一直是紅色,只有在 Derived Data 目錄下才能找到編譯生成的 .framework 文件。

關於other linker flag

使用靜態庫或者動態庫的時候極易發生連接錯誤,並且大多發生在加載framework中category的狀況!根本緣由在於Objective-C的連接器並不會爲每一個方法創建符號表,而是僅僅爲類創建了符號表。這樣的話,若是靜態庫中定義了已存在的一個類的分類,連接器就會覺得這個類已經存在,不會把分類和核心類的代碼合起來。這樣的話,在最後的可執行文件中,就會缺乏分類裏的代碼,這樣函數調用就失敗了。常見的設置方法就是在other linker flag中添加一個語句:-all_load,可是這樣也並非萬能的,具體解析請參考連接:http://my.oschina.net/u/728866/blog/194741

注意:當flag裏面添加了註釋卻仍是沒法使用的時候,可能報flag與bitcode衝突的問題尤爲是第三方庫可能和bitcode衝突),這樣的話就須要將bitcode設置爲NO!

bitcode的具體做用不作詳談,可參考:http://www.jianshu.com/p/3e1b4e2d06c6

簽名

系統在加載動態庫時,會檢查 framework 的簽名,簽名中必須包含 TeamIdentifier 而且 framework 和 host app 的 TeamIdentifier 必須一致。

若是不一致,不然會報下面的錯誤:

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

此外,若是用來打包的證書是 iOS 8 發佈以前生成的,則打出的包驗證的時候會沒有 TeamIdentifier 這一項。這時在加載 framework 的時候會報下面的錯誤:

[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

能夠經過 codesign 命令來驗證。

codesign -dv /path/to/YourApp.app

若是證書太舊,輸出的結果以下:

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=2015年10月21日 上午10:18:37
Info.plist entries=42
TeamIdentifier=not set
Sealed Resources version=2 rules=12 files=2451
Internal requirements count=1 size=188

注意其中的 TeamIdentifier=not set。

採用 swift 加載 libswiftCore.dylib 這個動態庫的時候也會遇到這個問題,對此Apple 官方的解釋是:

To correct this problem, you will need to sign your app using code signing certificates with the Subject Organizational Unit (OU) set to your Team ID. All Enterprise and standard iOS developer certificates that are created after iOS 8 was released have the new Team ID field in the proper place to allow Swift language apps to run.

If you are an in-house Enterprise developer you will need to be careful that you do not revoke a distribution certificate that was used to sign an app any one of your Enterprise employees is still using as any apps that were signed with that enterprise distribution certificate will stop working immediately.

只能經過從新生成證書來解決這個問題。可是 revoke 舊的證書會使全部用戶已經安裝的,用該證書打包的 app 沒法運行。

等等,咱們就跪在這裏了嗎?!

如今企業證書的有效期是三年,當證書過時時,其打包的應用就不能運行,那企業應用怎麼來更替證書呢?

Apple 爲每一個帳號提供了兩個證書,這兩個證書能夠同時生效,這樣在正在使用的證書過時以前,可使用另一個證書打包發佈,讓用戶升級到新版本。

也就是說,可使用另一個證書來打包應用,而且能夠覆蓋安裝使用舊證書打包的應用。詳情能夠看 Apple 文檔

深刻理解iPhone靜態庫

在實際的編程過程當中,一般會把一些公用函數製成函數庫,供其它程序使用,一則提搞了代碼的複用;二則提搞了核心技術的保密程度。因此在實際的項目開發中,常常會使用到函數庫,函數庫分爲靜態庫和動態庫兩種。和多數人所熟悉的動態語言和靜態語言同樣,這裏的所謂靜態和動態是相對編譯期和運行期的:靜態庫在程序編譯時會被連接到目標代碼中,程序運行時將再也不須要改靜態庫;而動態庫在程序編譯時並不會被連接到目標代碼中,只是在程序運行時才被載入,由於在程序運行期間還須要動態庫的存在。

深刻理解framework(框架,至關於靜態框架,不是動態庫)

打包framework仍是一個比較重要的功能,能夠用來作一下事情:

  • 封裝功能模塊,好比有比較成熟的功能模塊封裝成一個包,而後之後本身或其餘同事用起來比較方便。
  • 封裝項目,有時候會遇到這個狀況,就是一家公司找了兩個開發公司作兩個項目,而後要求他們的項目中的一個嵌套進另外一個項目,此時也能夠把唄嵌套的項目打包成framework放進去,這樣比較方便。

咱們爲何須要框架(Framework)?

要想用一種開發者友好的方式共享庫是很麻煩的。你不只僅須要包含庫自己,還要加入全部的頭文件,資源等等。

蘋果解決這個問題的方式是框架(framework)。基本上,這是含有固定結構幷包含了引用該庫時所必需的全部東西的文件夾。不幸的是,iOS禁止全部的動態庫。同時,蘋果也從Xcode中移除了建立靜態iOS框架的功能。

Xcode仍然能夠支持建立框架的功能,重啓這個功能,咱們須要對Xcode作一些小小的改動。

把代碼封裝在靜態框架是被app store所容許的。儘管形式不一樣,本質上它仍然是一種靜態庫。

框架(Framework)的類別

大部分框架都是動態連接庫的形式。由於只有蘋果才能在iOS設備上安裝動態庫,因此咱們沒法建立這種類型的框架。

靜態連接庫和動態庫同樣,只不過它是在編譯時連接二進制代碼,所以使用靜態庫不會有動態庫那樣的問題(即除了蘋果誰也不能在iOS上使用動態庫)。

「僞」框架是經過破解Xcode的目標Bundle(使用某些腳本)來實現的。它在表面上以及使用時跟靜態框架並沒有區別。「僞」框架項目的功能幾乎和真實的框架項目沒有區別(不是所有)。

「嵌入」框架是靜態框架的一個包裝,以便Xcode能獲取框架內的資源(圖片、plist、nib等)。 本次發佈包括了建立靜態框架和「僞」框架的模板,以及兩者的「嵌入」框架。

用哪種模板?

本次發佈有兩個模板,每一個模板都有「強」「弱」兩個類別。你能夠選擇最適合一種(或者兩種都安裝上)。 最大的不一樣是Xcode不能建立「真」框架,除非你安裝靜態框架文件xcspec在Xcode中。這真是一個遺憾(這個文件是給項目使用的,而不是框架要用的)。

簡單說,你能夠這樣決定用哪種模板:

  • 若是你不想修改Xcode,那麼請使用「僞」框架版本
  • 若是你只是想共享二進制(不是項目),兩種均可以
  • 若是你想把框架共享給不想修改Xcode的開發者,使用「僞」框架版本
  • 若是你想把框架共享給修改過Xcode的開發者,使用「真」框架版本
  • 若是你想把框架項目做爲另外一個項目的依賴(經過workspace或者子項目的方式),請使用「真」框架(或者「僞」框架,使用-framework——見後)
  • 若是你想在你的框架項目中加入其餘靜態庫/框架,並把它們也連接到最終結果以便不須要單獨添加到用戶項目中,使用「僞」框架

「僞」框架

「僞」框架是破解的「reloacatable object file」(可重定位格式的目標文件, 保存着代碼和數據,適合於和其餘的目標文件鏈接到一塊兒,用來建立一個可執行目標文件或者是一個可共享目標文件),它可讓Xcode編譯出相似框架的東西——其實也是一個bundle。

「僞框架」模板把整個過程分爲幾個步驟,用某些腳本去產生一個真正的靜態框架(基於靜態庫而不是reloacatable object file)。並且,框架項目仍是把它定義爲wrapper.cfbundle類型,一種Xcode中的「二等公民」。

所以它跟「真」靜態框架同樣能夠正常工做,但當存在依賴關係時就有麻煩了。

依賴問題

若是不使用依賴,只是建立普通的項目是沒有任何問題的。可是若是使用了項目依賴(好比在workspace中),Xcode就悲劇了。當你點擊「Link Binary With Libraries」下方的’+’按鈕時,「僞框架」沒法顯示在列表中。你能夠從你的「僞」框架項目的Products下面將它手動拖入,但當你編輯你的主項目時,會出現警告:

warning: skipping file '/somewhere/MyFramework.framework' (unexpectedfile type 'wrapper.cfbundle' in Frameworks & Libraries build phase) 並伴隨「僞」框架中的連接錯誤。

幸運的是,有個辦法來解決它。你能夠在」Other Linker Flags」中用」-framwork」開關手動告訴linker去使用你的框架進行連接:

-framework MyFramework

警告仍然存在,但起碼能正確連接了。

添加其餘的庫/框架

若是你加入其餘靜態(不是動態)庫/框架到你的「僞」框架項目中,它們將「連接」進你最終的二進制框架文件中。在「真」框架項目中,它們是純引用,而不是連接。

你能夠在項目中僅僅包含頭文件而不是靜態庫/框架自己的方式避免這種狀況(以便編譯經過)。

「真」框架

「真」框架各個方面都符合「真」的標準。它是真正的靜態框架,正如使用蘋果在從Xcode中去除的那個功能所建立的同樣。

爲了能建立真正的靜態框架項目,你必需在Xcode中安裝一個xcspec文件。

若是你發佈一個「真」框架項目(而不是編譯),但願去編譯這個框架的人必需也安裝xcspec文件(使用本次發佈的安裝腳本),以便Xcode能理解目標類型。

注意:若是你正在發佈徹底編譯的框架,而不是框架項目,最終用戶並不須要安裝任何東西。 我已經提交一個報告給蘋果,但願他們在Xcode中更新這個文件,但那須要一點時間。

加其餘靜態庫/框架

若是你加入其餘靜態(不是動態)庫/框架到你的「真」框架項目,它們只會被引用,而不會象「僞」框架同樣被連接到最終的二進制文件中。

從早期版本升級

若是你是從Mk6或者更早的版本升級,同時使用「真」靜態框架,而且使用Xcode4.2.1之前的版本,請運行uninstall_legacy.sh以卸載早期用於Xcode的全部修正。而後再運行install.sh,重啓Xcode。若是你使用Xcode4.3之後,只須要運行install.sh並重啓Xcode。

安裝

分別運行Real Framework目錄或Fake Framework目錄下的install.sh腳本進行安裝(或者兩個你都運行)。

重啓Xcode,你將在新項目嚮導的Framework&Library下看到StaticiOS Framework(或者Fake Static iOS Framework)。

卸載請運行unistall.sh腳本並重啓Xcode。

建立一個iOS框架項目

  1. 建立新項目。
  2. 項目類型選擇Framework&Library下的Static iOS Framework(或者Fake Static iOS Framework)。
  3. 選擇「包含單元測試」(可選的)。
  4. 在target中加入類、資源等。
  5. 凡是其餘項目要使用的頭文件,必需聲明爲public。進入target的Build Phases頁,展開Copy Headers項,把須要public的頭文件從Project或Private部分拖拽到Public部分。

編譯你的 iOS 框架

  1. 選擇指定target的scheme
  2. 修改scheme的Run配置(可選)。Run配置默認使用Debug,但在準備部署的時候你可能想使用Release。
  3. 編譯框架(不管目標爲iOS device和Simulator都會編譯出相同的二進制,所以選誰都無所謂了)。
  4. 從Products下選中你的framework,「show in Finder」。

在build目錄下有兩個文件夾:(yourframework).framework and (your framework).embeddedframework.

若是你的框架只有代碼,沒有資源(好比圖片、腳本、xib、coredata的momd文件等),你能夠把(yourframework).framework 分發給你的用戶就好了。若是還包含有資源,你必需分發(your framework).embeddedframework給你的用戶。

爲何須要embedded framework?由於Xcode不會查找靜態框架中的資源,若是你分發(your framework).framework, 則框架中的全部資源都不會顯示,也不可用。

一個embedded framework只是一個framework以外的附加的包,包括了這個框架的全部資源的符號連接。這樣作的目的是讓Xcode可以找到這些資源。

使用iOS 框架

OS框架和常規的Mac OS動態框架差很少,只是它是靜態連接的而已。

在你的項目中使用一個框架,只需把它拖僅你的項目中。在包含頭文件時,記住使用尖括號而不是雙引號括住框架名稱。例如,對於框架MyFramework:

import <MyFramework MyClass.h=""></MyFramework>

使用問題

Headers Not Found

若是Xcode找不到框架的頭文件,你多是忘記將它們聲明爲public了。參考「建立一個iOS框架項目」第5步。

No Such Product Type

若是你沒有安裝iOS Universal Framework在Xcode,並企圖編譯一個universal框架項目(對於「真」框架,不是「假」框架),這會致使下列錯誤:

target specifies product type 'com.apple.product-type.framework.static',but there's no such product type for the 'iphonesimulator' platform

爲了編譯「真」iOS靜態框架,Xcode須要作一些改動,所以爲了編譯「真」靜態框架項目,請在全部的開發環境中安裝它(對於使用框架的用戶不須要,只有要編譯框架才須要)。

The selected run destination is not valid for this action

有時,Xcode出錯並加載了錯誤的active設置。首先,請嘗試重啓Xcode。若是錯誤繼續存在,Xcode產生了一個壞的項目(由於Xcode4的一個bug,任何類型的項目都會出現這個問題)。若是是這樣,你須要建立一個新項目重來一遍。

連接警告

第一次編譯框架target時,Xcdoe會在連接階段報告找不到文件夾: ld: warning: directory not found for option'-L/Users/myself/Library/Developer/Xcode/DerivedData/MyFramework-ccahfoccjqiognaqraesrxdyqcne/Build/Products/Debug-iphoneos' 此時,能夠clean並從新編譯target,警告會消除。

Core Data momd not found

對於框架項目和應用程序項目,Xcode會以不一樣的方式編譯momd(託管對象模型文件)。Xcode會簡單地在根目錄建立.mom文件,而不會建立一個.momd目錄(目錄中包含VersionInfo.plist和.mom文件)。

這意味着,當從一個embedded framework的model中實例化NSManagedObjectModel時,你必需使用.mom擴展名做爲model的URL,而不是採用.momd擴展名。

NSURL *modelURL = [[NSBundle mainBundle]URLForResource:@"MyModel" withExtension:@"mom"];

Unknown class MyClass in Interface Builder file.

因爲靜態框架採用靜態連接,linker會剔除全部它認爲無用的代碼。不幸的是,linker不會檢查xib文件,所以若是類是在xib中引用,而沒有在O-C代碼中引用,linker將從最終的可執行文件中刪除類。這是linker的問題,不是框架的問題(當你編譯一個靜態庫時也會發生這個問題)。蘋果內置框架不會發生這個問題,由於他們是運行時動態加載的,存在於iOS設備固件中的動態庫是不可能被刪除的。 有兩個解決的辦法:

  • 讓框架的最終用戶關閉linker的優化選項,經過在他們的項目的Other Linker Flags中添加-ObjC和-all_load。

在框架的另外一個類中加一個該類的代碼引用。例如,假設你有個MyTextField類,被linker剔除了。假設你還有一個MyViewController,它在xib中使用了MyTextField,MyViewController並無被剔除。你應該這樣作:

在MyTextField中:

    • (void)forceLinkerLoad_ {}

在MyViewController中:

+(void) initialize { [MyTextField forceLinkerLoad_]; }

他們仍然須要添加-ObjC到linker設置,但不須要強制all_load了。

  • 這種方法須要你多作一點工做,但卻讓最終用戶避免在使用你的框架時關閉linker優化(關閉linker優化會致使object文件膨脹)。

unexpected file type 'wrapper.cfbundle' in Frameworks &Libraries build phase

這個問題發生在把「假」框架項目做爲workspace的依賴,或者把它看成子項目時(「真」框架項目沒有這個問題)。儘管這種框架項目產生了正確的靜態框架,但Xcode只能從項目文件中看出這是一個bundle,所以它在檢查依賴性時發出一個警告,並在linker階段跳過它。

你能夠手動添加一個命令讓linker在連接階段能正確連接。在依賴你的靜態框架的項目的OtherLinker Flags中加入:

-framework MyFramework

警告仍然存在, 但不會致使連接失敗。

Libraries being linked or not being linked into the finalframework

很不幸, 「真」框架和「假」框架模板在處理引入的靜態庫/框架的工做方式不一樣的。

「真」框架模板採用正常的靜態庫生成步驟,不會連接其餘靜態庫/框架到最終生產物中。

「假」框架模板採用「欺騙」Xcode的手段,讓它認爲是在編譯一個可重定位格式的目標文件,在連接階段就如同編譯一個可執行文件,把全部的靜態代碼文件連接到最終生成物中(儘管不會檢查是否確實目標代碼)。爲了實現象「真」框架同樣的效果,你能夠只包含庫/框架的頭文件到你的項目中,而不須要包含庫/框架自己。

Unrecognized selector in (some class with a category method)

若是你的靜態庫或靜態框架包含了一個模塊(只在類別代碼中聲明,沒有類實現),linker會搞不清楚,並把代碼從二進制文件中剔除。由於在最終生成的文件中沒有這個方法,因此當調用這個類別中定義的方法時,會報一個「unrecognizedselector」異常。

要解決這個,在包含這個類別的模塊代碼中加一個「假的」類。linker發現存在完整的O-C類,會將類別代碼連接到模塊。

我寫了一個頭文件 LoadableCategory.h,以減輕這個工做量:

import "SomeConcreteClass+MyAdditions.h"

import "LoadableCategory.h"

MAKE_CATEGORIES_LOADABLE(SomeConcreteClass_MyAdditions);

@implementation SomeConcreteClass(MyAdditions) ... @end

在使用這個框架時,仍然還須要在Build Setting的Other Linker Flags中加入-ObjC。

執行任何代碼前單元測試崩潰

若是你在Xcode4.3中建立靜態框架(或庫)target時,勾選了「withunit tests」,當你試圖運行單元測試時,它會崩潰:

Thread 1: EXC_BAD_ACCESS (code=2, address=0x0) 0 0x00000000 --- 15 dyldbootstrap:start(...)

這是lldb中的一個bug。你能夠用GDB來運行單元測試。編輯scheme,選擇Test,在Info標籤中將調試器Debugger從LLDB改成GDB。

 
 
轉載自:https://www.gitbook.com/book/leon_lizi/-framework-/details
相關文章
相關標籤/搜索