iOS開發:Xcode打包framework

來源:互聯網
上載者:User

   本人覺得這個打包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).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

  使用問題

  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。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.