前言
以下所涉及的框架和庫只針對(duì)iOS而言,不確保在其他平臺(tái)也適用。
最近由于公司業(yè)務(wù)需要,要求封裝一個(gè)支付SDK,需要用到微信支付和支付寶,之前做過的Framework沒有依賴其他第三方的庫所以比較好做,這次有所不同;一開始我想把支付寶和微信支付的SDK全部融合進(jìn)來,折騰一天才發(fā)現(xiàn)我之前的想法有很多誤區(qū),這樣是根本行不通的,不過最后還是封裝成功了,下面把我的經(jīng)驗(yàn)分享出來,供有需要的同學(xué)少走彎路。 制作之前最好把功課做足,看看靜態(tài)庫和動(dòng)態(tài)庫到底是什么東西。
轉(zhuǎn)載請(qǐng)注明出處:來自LeonLei的博客http://www.gaoshilei.com
一. 靜態(tài)庫和動(dòng)態(tài)庫的詳細(xì)介紹
我們平時(shí)的工程中或多或少都要引入第三方的SDK,就算你沒有引入第三方的,至少引用過系統(tǒng)的Framework吧?其實(shí)這些SDK和Framework都屬于庫,庫又分為靜態(tài)庫和動(dòng)態(tài)庫,我們平時(shí)導(dǎo)入的第三方SDK有的是Framework,有的是.a,到底哪些是動(dòng)態(tài)庫,哪些是靜態(tài)庫呢?下面分別介紹靜態(tài)庫、動(dòng)態(tài)庫,F(xiàn)ramework和.a以及.dylib/.tbd區(qū)別
一. 靜態(tài)庫與動(dòng)態(tài)庫
首先要解釋一下什么是庫,庫(Library)其實(shí)就是一段編譯好的二進(jìn)制代碼,加上頭文件就可以供別人使用,一般會(huì)有兩種情況要用到庫:
- 某些代碼需要給別人使用,但是我們不希望別人看到源碼,就需要以庫的形式進(jìn)行封裝,只暴露出頭文件。
- 對(duì)于某些不會(huì)進(jìn)行大的改動(dòng)的代碼,比方說很多大公司常用且很少變動(dòng)的模塊都會(huì)編譯成庫,這樣做的好處一是可以節(jié)省編譯時(shí)間,二來對(duì)于代碼的管理也非常方便。
因?yàn)閹焓且呀?jīng)編譯好的二進(jìn)制文件了,編譯的時(shí)候只需要link一下,既然提到了link那就有不同的形式了,靜態(tài)和動(dòng)態(tài),與之相對(duì)應(yīng)的就是靜態(tài)庫和動(dòng)態(tài)庫。
1. 靜態(tài)庫
平時(shí)我們用的第三方SDK基本上都是靜態(tài)庫,靜態(tài)庫的幾個(gè)特點(diǎn):
- 在App項(xiàng)目編譯的時(shí)候會(huì)被拷貝一份編譯到目標(biāo)程序中,相當(dāng)于將靜態(tài)庫嵌入了,所以得到的App二進(jìn)制文件會(huì)變大。
- 在使用的時(shí)候,需要手動(dòng)導(dǎo)入靜態(tài)庫所依賴的其他類庫。(比如說某個(gè)SDK中使用到了CoreMotion.framework,在使用的時(shí)候需要手動(dòng)導(dǎo)入。有的SDK需要link十幾個(gè)系統(tǒng)庫,這個(gè)時(shí)候非常惡心,只能一個(gè)一個(gè)手動(dòng)加,這是靜態(tài)庫一個(gè)很大的不便之處。)
- 導(dǎo)入靜態(tài)庫的應(yīng)用可以減少對(duì)外界的依賴,如果導(dǎo)入的是第三方動(dòng)態(tài)庫,動(dòng)態(tài)庫找不到的話應(yīng)用就會(huì)崩掉,例如Linux上經(jīng)常出現(xiàn)的lib not found。
- 靜態(tài)庫很大的一個(gè)優(yōu)點(diǎn)是減少耦合性,因?yàn)殪o態(tài)庫中是不可以包含其他靜態(tài)庫的,使用的時(shí)候要另外導(dǎo)入它的依賴庫,最大限度的保證了每一個(gè)靜態(tài)庫都是獨(dú)立的,不會(huì)重復(fù)引用。
2. 動(dòng)態(tài)庫
這個(gè)是我們最常用的一類庫,使用頻率最高的UIKit.framework和Fundation.framework都屬于動(dòng)態(tài)庫,所有.dylib和.tbd結(jié)尾的都屬于動(dòng)態(tài)庫。動(dòng)態(tài)庫的幾個(gè)特點(diǎn):
- 平時(shí)使用的系統(tǒng)庫都放在iOS系統(tǒng)中,在你打包應(yīng)用程序的時(shí)候這些庫不會(huì)拷貝到你的程序中,當(dāng)需要使用的時(shí)候會(huì)動(dòng)態(tài)從iOS系統(tǒng)中加載它們,因?yàn)檫@個(gè)原因,動(dòng)態(tài)庫也被稱作共享庫。編譯時(shí)才載入的特性,也可以讓我們隨時(shí)對(duì)庫進(jìn)行替換,而不需要重新編譯代碼。
- 這些庫是所有應(yīng)用公用的,換一種說法就是節(jié)省了應(yīng)用安裝包的體積,這是區(qū)別靜態(tài)庫很重要的一個(gè)特點(diǎn),因?yàn)殪o態(tài)庫使用一次就要拷貝一次,非常浪費(fèi)資源。
- 動(dòng)態(tài)庫在制作的時(shí)候可以直接包含靜態(tài)庫,也能自動(dòng)link所需要的依賴庫。
- 使用動(dòng)態(tài)庫的時(shí)候不需要再次link依賴庫,即導(dǎo)即用,這個(gè)就厲害了。唯一需要注意的是在導(dǎo)入自己制作的動(dòng)態(tài)庫時(shí),需要在Embedded Binaries中導(dǎo)入,不然會(huì)報(bào)錯(cuò):image not found。此時(shí)這個(gè)動(dòng)態(tài)庫會(huì)跟靜態(tài)庫一樣被拷貝到目標(biāo)程序中進(jìn)行編譯,蘋果又把這種Framework叫做Embedded Framework
關(guān)于動(dòng)態(tài)庫要搞清楚一點(diǎn),我們自己制作的動(dòng)態(tài)庫與系統(tǒng)動(dòng)態(tài)庫的區(qū)別,我們自己制作的動(dòng)態(tài)庫引入App項(xiàng)目的時(shí)候需要embed進(jìn)項(xiàng)目,也就是要拷貝到目標(biāo)程序中,這就有點(diǎn)不像動(dòng)態(tài)庫的特性了,蘋果這么做也是考慮安全問題吧!
至于能不能正常上架,我也不清楚,查了大量資料都是抄來抄去沒說清楚,我猜測(cè)是不能上架的,因?yàn)橐话愕牡谌絊DK也都是靜態(tài)庫的形式,我猜測(cè)一個(gè)重要原因是iOS的應(yīng)用本來就是運(yùn)行在沙盒里面的,不同應(yīng)用之間不能共享代碼,同時(shí)動(dòng)態(tài)下載代碼蘋果肯定也是明令禁止的,所以動(dòng)態(tài)庫也就失去意義了。當(dāng)然可能還有其他因素,歡迎交流學(xué)習(xí)!
二. Framework、.a、.dylib/.tbd
1. Framework
Framework的英文釋意是框架,主要由Headers、binary文件、.bundle這三部分構(gòu)成,除此之外還有Info.plist和Modules,后兩者主要記錄Framework的版本之類的信息,一般都會(huì)刪掉,不做討論
-
Headers
包含我們?cè)谥谱鱂ramework的時(shí)候暴露的頭文件,所有被暴露的.h都放在這里。 -
binary文件
整個(gè)Framework的核心,所有代碼都被編譯成了這樣一坨二進(jìn)制文件,這里要注意的是添加的依賴庫不會(huì)被編譯進(jìn)來,用的時(shí)候還需要重新link其他依賴庫。 -
.bundle
資源文件都打包放在這里。在制作Framework的時(shí)候不可以把圖片直接放在項(xiàng)目中,否則制作好之后圖片是一張一張的出現(xiàn)在項(xiàng)目中非常亂,需要新建一個(gè)bundle將圖片放進(jìn)去,這里的bundle提供整個(gè)SDK的圖片資源。
注意:圖片放進(jìn)bundle之后不可以用[UIImage ImageWithName:]
讀取圖片。要先找到bundle包再拿圖片。
這里要糾正一個(gè)誤區(qū)
很多人認(rèn)為系統(tǒng)的Framework就是動(dòng)態(tài)庫,我們自己制作的Framework就是靜態(tài)庫。
其實(shí)Framework既可以是靜態(tài)庫也可以是動(dòng)態(tài)庫,這取決于編譯成的Mach-O(就是那個(gè)二進(jìn)制文件)是動(dòng)態(tài)庫還是靜態(tài)庫,F(xiàn)ramework本質(zhì)上并不是一個(gè)庫,它是蘋果為了方便開發(fā)者提供了一種庫的打包方式,F(xiàn)ramework會(huì)將Mach-O文件、頭文件和資源包全都包含進(jìn)來,不需要你再手動(dòng)整理,我們也可以通過Xcode來制作framework動(dòng)態(tài)庫使用。
所以總結(jié): Framework是庫的打包形式,既可以是動(dòng)態(tài)庫也是靜態(tài)庫。
2. .a靜態(tài)庫
這類靜態(tài)庫與Framework基本類似,不同的是在打包成.a文件的同時(shí),還需要提供頭文件,使用時(shí)相較于Framework比較麻煩,(例如微信支付SDK使用的是.a,不同的是支付寶SDK是以framework的形式打包的)。.a這樣打包不夠方便,而Framework編譯完成暴露的頭文件都已經(jīng)放好了。
3. .dylib/.tbd 動(dòng)態(tài)庫
這類動(dòng)態(tài)庫我們也經(jīng)常用,基本上都是系統(tǒng)提供的,一般不能自己制作,就算你通過其他方式制作使用,也肯定不能上架的,這里沒什么好講的。
二. Framework(靜態(tài)庫+動(dòng)態(tài)庫)的制作
動(dòng)態(tài)庫與靜態(tài)庫的制作流程基本一樣,包括頭文件的暴露等,唯一不同的是Mach-O文件的編譯形式。本節(jié)將介紹Xcode制作Framework的過程,本次制作的Framework靜態(tài)庫依賴其他第三方靜態(tài)庫(Framework和.a)。
1> 新建工程
這里要選Framework,如果選擇右邊的Static Library制作出來的是.a靜態(tài)庫。
2> 導(dǎo)入所有要打包的文件和其他第三方靜態(tài)庫
正常導(dǎo)入要打包的文件就可以了,在導(dǎo)入第三方靜態(tài)庫的時(shí)候要注意,不要選擇添加到target中,如果添加進(jìn)去要去target里面把第三方靜態(tài)庫刪掉(只需導(dǎo)入,不要添加進(jìn)target)
導(dǎo)入第三方靜態(tài)庫之后再link依賴的系統(tǒng)庫,像這樣
注意上面的運(yùn)行目標(biāo),因?yàn)槲矣玫氖荴code8,最低支持到iOS8。
要打包的文件和第三方靜態(tài)庫全部導(dǎo)入完成
3> 項(xiàng)目性質(zhì)修改
把項(xiàng)目的membership需改為public,否則頭文件暴露將會(huì)不正常
4> 暴露頭文件
將頭文件暴露出去,供外界使用,所有的編譯文件都在Project中,需要右擊添加到public里面
5> 選擇Mach-O的編譯方式
這是最重要的一步,這一步?jīng)Q定我們制作出來的是靜態(tài)庫還是動(dòng)態(tài)庫,默認(rèn)選擇的是Dynamic Library,要手動(dòng)選擇Static Library
6> 編譯
如果你的依賴庫里面有l(wèi)ib開頭的dylib動(dòng)態(tài)庫,此時(shí)應(yīng)該會(huì)報(bào)錯(cuò)
什么意思呢?大概就是沒找到對(duì)應(yīng)的庫文件,因?yàn)閠bd是蘋果提供的新的動(dòng)態(tài)庫格式,之前都是dylib,不知道這里又抽什么風(fēng),下面解決問題。
7> tbd動(dòng)態(tài)庫報(bào)錯(cuò)修改
先把原來的.tbd刪掉,然后再次添加,這個(gè)時(shí)候選擇add other,在彈出的窗口中按快捷鍵shift + command + G 調(diào)出finder的前往窗口,輸入/usr/lib,然后添加相應(yīng)的dylib動(dòng)態(tài)庫
替換完成之后重新編譯項(xiàng)目,生成Framework(可在Product文件中右擊在finder中顯示找到)
8> 使用
新建一個(gè)文件夾,將制作好的靜態(tài)庫拷貝出來放進(jìn)去,再將第三方靜態(tài)庫拷貝到相同的文件夾中,此時(shí)只要將這個(gè)文件夾提供給外界使用就可以了,這是我寫的測(cè)試demo驗(yàn)證打包好的SDK是否可以正常使用
至此我們已經(jīng)完成了Framework中包含其他第三方靜態(tài)庫的制作。
如果需要制作動(dòng)態(tài)庫,只需要在第5步中將Mach-O的形式改為Dynamic Library就可以了,其他步驟一樣
如果有問題請(qǐng)?jiān)诹粞詤^(qū)留言,或者郵件給我,互相交流學(xué)習(xí)!