若是你想將你開發的控件與別人分享,一種方法是直接提供源代碼文件。然而,這種方法並非很優雅。它會暴露全部的實現細節,而這些實現你可能並不想開源出來。此外,開發者也可能並不想看到你的全部代碼,由於他們可能僅僅但願將你的這份漂亮代碼的一部分植入本身的應用中。html
另外一種方法是將你的代碼編譯成靜態庫(library),讓其餘開發者添加到本身的項目中。然而,這須要你一併公佈全部的公開的頭文件,實在是很是不方便。ios
你須要一種簡單的方法來編譯你的代碼,這種方法應該使得你的代碼易分享,而且在多個工程中易複用。你須要的是一種方法來打包你的靜態庫,將全部的頭文件放到一個單元中,這樣你就能夠馬上將其加入到你的項目中並使用。xcode
OS X完美地支持這一點,由於Xcode就提供了一個項目模板,包含着默認構建目標(target)和能夠容納相似於圖片、聲音、字體等資源的文件。你能夠爲iOS建立Framework,不過這是一個比較複雜的手工活,若是你跟着教程走,你將學到怎麼樣跨過路障,順利地完成Framework的建立。iphone
能夠參考這篇文章.a和.framework.a和.framework的區別。函數
咱們能夠看出.a的封裝和.framework的封裝差很少,也有模擬器和真機合併的過程,經過上邊的圖片咱們能夠看出.a 和.framework的區別,就是.a+.h+soureFile=.framework。能夠看出咱們直接封裝.framework實際上是最好的。那麼咱們就來看看framework怎麼封裝的。測試
另外關於.a的封裝你們能夠參考iOS如何生成.a文件字體
本文將基於Xcode7建立一個簡單的工程,經過兩種方法來教你們如何製做一個本身的framework,目的就是簡單易學的製做framework。這種方法可使得你的代碼易分享,在多個工程中複用,而且能夠隱藏實現細節,控制公開的頭文件。ui
一、打開Xcode,新建工程。spa
不要選擇「Application」,選擇「Framework & Library」。選擇第一個,而後Next。.net
二、建立功能類。
這裏我建立一個繼承自NSObject的SayHello類
三、實現功能。
在新建立的類裏面聲明方法並實現。這裏我寫一個sayHello的方法,以便後面測試使用。
四、更改參數
在TARGETS下選中工程,在Build Settings下更改幾個參數。
五、增長armv7s
在Architectures下增長armv7s,並選中。將Build Active Architecture Only 設置爲NO。
六、設置Headers
將你要公開的頭文件拖至Public下,要隱藏的放在Private或者Project下,固然,隱藏的頭文件就沒法再被引用。
而後須要在Test.h(必須是公開的,不然沒法引用)中將你全部要公開的.h引入。
打包Framework
1.選中模擬器,編譯程序
2.選中測試機,編譯程序
3.在finder中找到framework文件
找到下圖中所示的Test文件,一個是Debug-iphoneos(真機)下的,一個是Debug-iphonesimulator(模擬器)下的。
4.經過終端命令將兩個framework合爲一個模擬器和真機均可使用的framework。
打開控制檯輸入 lipo -create iphoneos下frameworkTest的路徑 simulator下frameworkTest的路徑 -output 新的路徑,這樣就完成了模擬器和真機版本的合併,新路徑下的frameworkTest就是你合併後的文件,將這個文件名字改爲和你未合併以前的Test同樣的名字,放到framework文件夾下,替換掉原來的frameworkTest文件。
上邊說的亂糟糟的,看不清楚,這裏給你們解釋一下,看下邊的圖:打開終端,手動輸入畫紅線的lipo -create命令,而後綠線是iphoneos下frameworkTest的路徑(找到iphoneos下frameworkTest的文件,拖拽進來),會自動有空格,紫線是simulator下frameworkTest的路徑(一樣找到simulator下frameworkTest的文件,拖拽進來),也會自動有空格,而後輸入-output,而後敲空格,在引入一個新的路徑(拖拽進一個新的路徑),最後敲回車。這樣就完成合並了。
上面這段命令就是把真機和模擬器的frameworkTest合併成一個MyNewFrameworktest文件並存放在桌面上的New文件夾下。
這裏咱們合併的時候會遇到一個error,這是啥緣由還真不知道,可是會在和咱們-output的文件夾路徑並列的地方生成一個.lipo文件,這個.lipo文件咱們下邊會說到。
注意:合併完成後會出現一個以下圖的.lipo格式的文件。
這TM是啥,不是應該出現一個相似下圖的嗎?不該該後綴什麼也沒有嗎?怎麼後綴會是.lipo,這是什麼文件啊?!
咱們的操做是按照人家說的把合成後的文件名字改爲MyFrameworkTest替換原來的。並且,把後綴.lipo去掉!
在按照上述說的,替換了原來的。
而後就能夠進行下一步了。
5.將修改後的framework拷貝出來保存,這就是咱們最終制做的framework。
一、選中TARGETS下的工程,點擊上方的Editor,選擇Add Target建立一個Aggregate.
二、選擇Other下的Aggregate,點擊Next建立。
三、嵌入腳本。選中剛剛建立的Aggregate,而後選中右側的Build Phases,點擊左下方加號,選擇New Run Script Phase
將這段腳本複製進去:
# Sets the target folders and the finalframework product.# 若是工程名稱和Framework的Target名稱不同的話,要自定義FMKNAME# 例如: FMK_NAME = "MyFramework"FMK_NAME=${PROJECT_NAME}# Install dir will be the final output tothe framework.# The following line create it in the rootfolder of the current project. INSTALL_DIR=${SRCROOT}/Products/$ {FMK_NAME}.framework# Working dir will be deleted after theframework creation. WRK_DIR=build DEVICE_DIR=${WRK_DIR}/Release-iphoneos/$ {FMK_NAME}.framework SIMULATOR_DIR=${WRK_DIR}/Release- iphonesimulator/${FMK_NAME}.framework# -configuration ${CONFIGURATION}# Clean and Building both architectures.xcodebuild -configuration"Release"-target"${FMK_NAME}"-sdk iphoneos clean build xcodebuild -configuration"Release"-target"${FMK_NAME}"-sdk iphonesimulator clean build# Cleaning the oldest.if[-d"${INSTALL_DIR}"]thenrm -rf"${INSTALL_DIR}"fimkdir -p"${INSTALL_DIR}"cp -R"${DEVICE_DIR}/""${INSTALL_DIR}/"# Uses the Lipo Tool to merge both binaryfiles (i386 + armv6/armv7) into one Universal final product. lipo -create"${DEVICE_DIR}/${FMK_NAME}""${SIMULATOR_DIR}/${FMK_NAME}"-output"$ {INSTALL_DIR}/${FMK_NAME}"rm -r"${WRK_DIR}"open"${INSTALL_DIR}"
這裏有一個誤區,就是複製上邊的這段腳本的時候,會在咱們指望的效果裏面多了幾個回車,這幾個回車是致命的,若是不刪除回車,會報出以下的錯誤:
最後的格式以下圖,儘可能一個回車也不能錯:
經過第一種方法中「把真機和模擬器的frameworkTest合併成一個」的過程和上邊的腳本語言比較,咱們能夠發現其實二者異路同歸,兩個方法裏面同時用到了「lipo -create xxx」和「-output xxx」,不一樣的地方是第一種方法須要咱們本身真機和模擬器分別變異一遍,並且須要咱們把framework的路徑拖進去,相比而言第二種方法比較簡單。
四、編譯。如圖所示,command+B編譯。這裏Generic iOS Device的意思是「iOS通用設備」,大概就是說模擬器和真機都能用。
五、編譯成功後會自動跳出一個finder,保存這個.framework,這就是咱們須要的framework。
至此,兩種打包framework的方法介紹完成!
最後就是用咱們的Framework了,倒入另外一個Xcode中,咱們打開這個framework看看,發現只有Headers,裏面有兩個.h,其中一個是咱們以前添加的FrameworkDemo.h文件,另外一個就是咱們的SayHello.h 。
而後引入頭文件:
因爲咱們測試的方法是實例方法,那麼咱們實例化一個實例對象,而後就可讓這個實例對象調取相應的方法了:
至此,完成Framework的製做和使用。
最後須要注意的是:
一、.h文件的外漏必定要保證是本身的想要外漏的。不想外漏的就別外漏了。
二、開始打包的時候,必定要在選中模擬器和選中真機上邊分別編譯一次, 我以爲以前在家裏沒有真機的時候編譯的好像不對。
三、在終端上邊合併的時候多是error並生成一個.lipo文件,不要怕,大膽修改爲同名的不掛後綴的同名文件。
四、調用的時候分清楚是類方法仍是實例方法,方便調用。
五、在製做framework或者lib的時候,若是使用了category,則使用改FMWK的程序運行時會crash,此時須要在該工程中 other linker flags添加兩個參數 -ObjC -all_load。(這點沒有親測)
六、帶有圖片資源的須要把圖片打包成Bundle文件,和framework一塊兒拷貝到相應的項目中。
七、公開的類中若是引用的private的類,打包之後對外會報錯,找不到那個private的類,能夠把那個private的.h放到(也沒親測)
八、namespace 衝突。靜態庫用了某第三方庫,項目也用了一樣的第三方庫,在編譯的時候就會有 duplicate symbol 錯誤,由於有兩份一樣的第三方庫。解決辦法就是把用到的第三方庫加上自定義前綴,包括類名、delegate 協議、常量名,尤爲須要注意 Category 的方法名要修改。
九、封裝靜態庫的時候應儘可能避免引入重量級第三方庫,多本身進行封裝。
十、一個靜態庫要有本身獨有的前綴,全部類名、常量等都要加一樣的前綴。
十一、真機+模擬器支持。(和第2條意思同樣)Xcode 默認只會用當前環境(真機或模擬器)生成靜態庫,這樣的 SDK 不方便其餘項目開發時調試。解決辦法就是經過腳本生成一份通用庫,build_universal_library.sh,via SO.
十二、文檔。靜態庫的方即是使用者直接拿你提供的方法來用,無需關注具體實現;不方便在於看不到實現,出現問題沒法排查,所以須要把 SDK 的版本、更新歷史、使用、FAQ 等寫成文檔,方便使用,也顯得 SDK 比較正式規範。
1三、圖片等資源文件用 bundle 方式打包。一個簡單製做 bundle 的方法:新建文件夾,重命名爲 YourSDK.bundle,而後 Show Package Contents 打開,加入圖片。使用圖片的時候須要指明 bundle: [UIImage imageNamed:@"YourSDK.bundle/img.png"]。也能夠用 Target 方式製做 bundle,好比 iOS Library With Resourceshttp://www.galloway.me.uk/tutorials/ios-library-with-resources/.
1四、若是 SDK 有用到 Category,注意項目設置 Other Linker Flags 添加 -ObjC。(後邊介紹了-ObjC的做用)
編譯過程:
從C代碼到可執行文件經歷的步驟是:源代碼 > 預處理器 > 編譯器 > 彙編器 > 機器碼 > 連接器 > 可執行文件
在最後一步須要把.o文件和C語言運行庫連接起來,這時候須要用到ld命令。源文件通過一系列處理之後,會生成對應的.obj文件,而後一個項目必然會有許多.obj文件,而且這些文件之間會有各類各樣的聯繫,例如函數調用。連接器作的事就是把這些目標文件和所用的一些庫連接在一塊兒造成一個完整的可執行文件。Other linker flags設置的值實際上就是ld命令執行時後面所加的參數
下面逐個介紹3個經常使用參數:
-ObjC:加了這個參數後,連接器就會把靜態庫中全部的Objective-C類和分類都加載到最後的可執行文件中
-all_load:會讓連接器把全部找到的目標文件都加載到可執行文件中,可是千萬不要隨便使用這個參數!假如你使用了不止一個靜態庫文件,而後又使用了這個參數,那麼你頗有可能會遇到ld: duplicate symbol錯誤,由於不一樣的庫文件裏面可能會有相同的目標文件,因此建議在遇到-ObjC失效的狀況下使用-force_load參數。
-force_load:所作的事情跟-all_load實際上是同樣的,可是-force_load須要指定要進行所有加載的庫文件的路徑,這樣的話,你就只是徹底加載了一個庫文件,不影響其他庫文件的按需加載
後期會試着把貝塞爾畫餅的demo封裝成framework,另外可能會增長Bundle文件的生成方法。
參考自一、iOS-製做Framework(最新)
最後,哪裏不對的地方能夠給我留言,我會及時改進的,謝謝你們。