下文是轉載,本人以爲這個打包framework仍是一個比較重要的功能,能夠用來作一下事情:git
(1)封裝功能模塊,好比有比較成熟的功能模塊封裝成一個包,而後之後本身或其餘同事用起來比較方便。github
(2)封裝項目,有時候會遇到這個狀況,就是一家公司找了兩個開發公司作兩個項目,而後要求他們的項目中的一個嵌套進另外一個項目,此時也能夠把唄嵌套的項目打包成framework放進去,這樣比較方便。bootstrap
咱們爲何須要框架(Framework)?app
要想用一種開發者友好的方式共享庫是很麻煩的。你不只僅須要包含庫自己,還要加入全部的頭文件,資源等等。框架
蘋果解決這個問題的方式是框架(framework)。基本上,這是含有固定結構幷包含了引用該庫時所必需的全部東西的文件夾。不幸的是,iOS禁止全部的動態庫。同時,蘋果也從Xcode中移除了建立靜態iOS框架的功能。iphone
Xcode仍然能夠支持建立框架的功能,重啓這個功能,咱們須要對Xcode作一些小小的改動。單元測試
把代碼封裝在靜態框架是被app store所容許的。儘管形式不一樣,本質上它仍然是一種靜態庫。測試
框架(Framework)的類別優化
大部分框架都是動態連接庫的形式。由於只有蘋果才能在iOS設備上安裝動態庫,因此咱們沒法建立這種類型的框架。ui
靜態連接庫和動態庫同樣,只不過它是在編譯時連接二進制代碼,所以使用靜態庫不會有動態庫那樣的問題(即除了蘋果誰也不能在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).framework and (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 <MyFramework/MyClass.h>
使用問題
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)
...
在使用這個框架時,仍然還須要在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。