當(dāng)我們看好多第三方的插件的時(shí)候,會發(fā)現(xiàn)好多.a/.framework的靜態(tài)庫晦嵌,之所以這些大廠這么做,其實(shí)是為了方便在集成完畢之后拷姿,快速的編譯耍铜,最重要的是看不到自己的源碼,并且保護(hù)我們的代碼跌前。并且在MRC階段的代碼棕兼,如果想要在ARC下使用,那么確實(shí)需要進(jìn)行ARC兼容抵乓,但是我們進(jìn)行編譯之后伴挚,并不需要考慮這些問題,直接引用這個(gè)編譯好的靜態(tài)庫就行灾炭,并不需要兼容ARC茎芋,確實(shí)好處多多。
首先講解如何創(chuàng)建.a的二進(jìn)制文件
1.創(chuàng)建一個(gè)靜態(tài)庫工程
2.由于不同的模擬器以及真機(jī)對應(yīng)著不同的架構(gòu)蜈出,
模擬器:4-5:i386? ?5s-iphoneX:x86-64
真機(jī):3gs-4s: armv7(兼容7s)? ?5-5c: armv7s? ?5s-iphoneX:arm64
為了適配所有的CPU架構(gòu)我們設(shè)置如下田弥,Build Active Architecture Only? YES表示只編譯某些比較主流的架構(gòu),所以要想都進(jìn)行編譯铡原,我們需要對所有的都編譯
注意如果你用的是比較新的xcode版本,比如xcode10,那么由于并沒有自帶比較老的模擬器,比如4/4s等,那么生成出來的靜態(tài)庫架構(gòu)并不會包含i386或者armv7,如果想兼容4/4s等比較久的機(jī)型,需要下載老的模擬器版本
3.查看生成的.a文件架構(gòu)架構(gòu)
在終端中cd 到.a文件的父文件
輸入命令 lipo? -info? 靜態(tài)庫名稱.a
輸出當(dāng)前所有支持的架構(gòu)
4.暴露.h 文件
5.默認(rèn)生成的.a文件模擬器和真機(jī)是分開的偷厦,所以需要合成同時(shí)支持真機(jī)和模擬器的.a
lipo -create 模擬器中.a文件地址 真機(jī)中.a文件地址 -output? 新的文件名.a
6.生成realease版本的靜態(tài)庫(上面生成的是debug版本)
然后在項(xiàng)目中切換真機(jī)/模擬器,command+B燕刻,就會在.a文件的根目錄發(fā)現(xiàn)release/debug只泼、真機(jī)/模擬器版本信息
當(dāng)我們應(yīng)用.a文件的時(shí)候,只需要將import? .h文件進(jìn)來就可以直接使用靜態(tài)庫文件里的方法了卵洗。
接下來我們用framework來生成靜態(tài)庫
.a文件是一個(gè)純二進(jìn)制文件请唱,.frameWork中除了有二進(jìn)制文件還有其他的資源文件;.a文件必須有.h文件配合使用,frameWork文件可以直接使用十绑;framework靜態(tài)庫就類似一個(gè)文件夾聚至,所有的子類文件都要放到framework中,包括可執(zhí)行文件frameWork和.h以及資源文件本橙⊥砹耄可以這么總結(jié).a+.h+sourceFile(資源文件)=framework,所以相比.a文件來說,framework更加易于管理勋功,所以本人更傾向與生成framework靜態(tài)庫
同樣在新建工程中選擇
2.項(xiàng)目中添加一個(gè)工具類Tool坦报,當(dāng)Command+B編譯之后,在framework中發(fā)現(xiàn)只有frameWork.h的頭文件,并沒有我們想要的Tool.h,所以我們需要對Tool.h暴露,將暴露到外界的.h文件放到public目錄下,如果不用frameWork.h主頭文件,可以將其刪除
3.檢查可執(zhí)行文件支持的CPU架構(gòu)
注意cd到.frameWork,然后檢查目錄下frameWork可執(zhí)行文件中的架構(gòu),發(fā)現(xiàn)是x86_64,由于當(dāng)前是在模擬器上進(jìn)行的編譯,所以我們需要兼容所有的模擬器/真機(jī)類型.
修改當(dāng)前active Architecture Only的類型為No,表示所有的模擬器/真機(jī)架構(gòu)都需要編譯
接下來我們需要在XCode中選擇設(shè)備處設(shè)置為Generic ios Device,編譯后的結(jié)構(gòu)可以在項(xiàng)目中的Products->Framework.framework包中找到編譯之后的結(jié)構(gòu),包含真機(jī)和模擬器兩部分.
4.適配所有的CPU架構(gòu)(和上邊生成.a文件一樣)
5.生成release版本的.framework靜態(tài)庫,而不是debug版本的靜態(tài)庫(注意:當(dāng)前5到7步是生成了動態(tài)庫狂鞋,可以直接看7步中的原因片择,進(jìn)行相應(yīng)的步驟調(diào)整。這樣寫是為了更好的記錄遇到的問題)
6.合成適配模擬器和真機(jī)的靜態(tài)庫
注意合成的是靜態(tài)庫目錄下的可執(zhí)行文件(frameWork)的合并骚揍。
將地址拖過來就行
lipo -create 模擬器中可執(zhí)行文件地址 真機(jī)中可執(zhí)行文件地址 -output? 新的文件名.framework
將生成的frameWork字管,替換掉之前release版本里的可執(zhí)行文件,那么這個(gè)frameWork就可以用了信不。
7.修改frameWork(剛生成的時(shí)候其實(shí)為動態(tài)庫)為靜態(tài)庫
防止崩潰的發(fā)生嘲叔,我們需要將frameWork動態(tài)庫引用進(jìn)Embedded Binaries
所以,當(dāng)我們生成frameWork的時(shí)候就應(yīng)該生成靜態(tài)庫抽活。所以在生成frameWork工程中硫戈,
將Dynamic Library修改為 Static Library。所以上邊的第5到第7個(gè)步驟中我們需要重新進(jìn)行生成下硕。(刪除工程中Library Framework和 Embedded Binaries 中的動態(tài)庫文件)重新導(dǎo)入工程進(jìn)行測試丁逝,發(fā)現(xiàn)不將framework引入Embedded Binaries,也是可以正常運(yùn)行的梭姓,那么現(xiàn)在生成的是靜態(tài)庫了霜幼。
記錄一些問題:
(1).圖片資源的導(dǎo)入問題?(framework和.a圖片的導(dǎo)入方式是一樣的)
拿生成.a靜態(tài)庫舉例,添加資源文件,將圖片放入Tool.h同級目錄下,編譯,會發(fā)現(xiàn)我們編譯后的包中,并沒有我們的資源圖片,我們需要將圖片文件添加到BuildPhases->CopyFiles中+進(jìn)來,才會放到我們編譯后的文件中,包括三部分.a+.h+資源圖片,那么這個(gè)時(shí)候會出現(xiàn)一個(gè)問題:
將我們編譯好的.a+.h+資源文件作為一個(gè)靜態(tài)庫放入我們的主項(xiàng)目中,如果我們的主項(xiàng)目中已經(jīng)有一個(gè)同名圖片,然后對主項(xiàng)目進(jìn)行編譯后,發(fā)現(xiàn)包中只有一個(gè)同名的圖片.
為了解決這個(gè)問題,我們將圖片放到一個(gè)文件夾中,將這個(gè)文件夾放到和.a/.h同級目錄下,當(dāng)我們在主工程引用這個(gè)靜態(tài)庫的時(shí)候,需要將這個(gè)文件夾拖入主工程,但是我們用兩個(gè)選擇Create Group/Create folder references,由于我們將靜態(tài)庫導(dǎo)入到主工程,其實(shí)包括資源文件/.h/.a都已經(jīng)編譯好了,所以并不需要主工程對當(dāng)前的資源文件做出引用,應(yīng)該選擇Create folder references,這樣會將資源文件/.a/.h都加入到主工程的同級目錄下,否則會發(fā)現(xiàn)資源文件不見了.
但是如果選擇了Create Group,那么就會出問題,所以我們將資源文件放到bundle中進(jìn)行存放(),bundle中的資源并不會因?yàn)檫@兩個(gè)選項(xiàng)選錯(cuò)了就會把資源文件位置放錯(cuò),一直會放到同級目錄下.
(2).如果用戶導(dǎo)入的頭文件過多怎么辦?
我們自己先建立一個(gè)主頭文件,我們將所有的頭文件都導(dǎo)入到主頭文件中,我們直接import主頭文件就行
(3).靜態(tài)庫如何測試?
.a文件如果交給主工程測試無法定位問題所在
所以定義一個(gè)復(fù)合工程,進(jìn)行單元測試,在Targets中點(diǎn)擊+號,選擇Framework & Library 中的CocoaTouch Framework,新建一個(gè)Framework工程
比如我們想要在A業(yè)務(wù)模塊中調(diào)試代碼,那么我們將A業(yè)務(wù)模塊代碼放到主工程中,調(diào)用我們想要編譯成靜態(tài)庫的代碼B,將B(以Tool類為例)
在復(fù)合工程FrameworkTests中引入我們的靜態(tài)庫類,然后再主工程中調(diào)用Tool.h類進(jìn)行測試,如果沒問題,那么在Targets點(diǎn)中我們的復(fù)合工程Testframework,配置生成靜態(tài)庫的某些項(xiàng),編譯完之后,再Products中查找Testframework并查看包的內(nèi)容,生成合并的release靜態(tài)庫.
(4).當(dāng)我們將組建進(jìn)行二進(jìn)制化之后,那么導(dǎo)入方式將會改變,例如#import 'Tool.h'? #import <Framework/Tool.h>,如何避免多地方的改動?
我們可以將.h文件剝離出來,獨(dú)立于framework之外,引用的時(shí)候直接調(diào)用#import 'Tool.h',實(shí)現(xiàn)的時(shí)候還是在framework中進(jìn)行實(shí)現(xiàn)
(5).如何將生成的所有的架構(gòu)文件刪除某個(gè)架構(gòu),該如何操作?比如我們不需要支持i386
第一種:那么我們分別對其他的架構(gòu)生成靜態(tài)庫,然后將靜態(tài)庫合并
第二種:直接在生成的靜態(tài)庫中移除某一個(gè)架構(gòu), cd到當(dāng)前靜態(tài)庫的上一級,命令? ? ?
?lipo -remove i386 文件地址.a? -output? 生成的名字.a
如果只要某一個(gè)架構(gòu)的文件 比如arm64:
lipo -thin arm64 文件地址.a -output 生成名.a
接下來我們將framework放到遠(yuǎn)端倉庫
以之前創(chuàng)建的一個(gè)WKDownLoad私有庫為例首先創(chuàng)建一個(gè)Framework 項(xiàng)目WKDownloadLb,將WKDownLoad中的代碼放到WKDownloadLb中,并暫時(shí)刪除自帶的WKDownloadLb.h文件,Xcode配置相應(yīng)的生成framework的某些項(xiàng),mach-o-Type/Build Active Architecture Only/run->release
接下來,為了能夠方便的查找我們生成framework位置,需要對xcode進(jìn)行配置File->Project Settings
將framework存放到相對于workspace的同級目錄下的products文件下,開始進(jìn)行編譯,然后將framework進(jìn)行合并,合并完之后,我們可以將WKDownloadLb.framework放到Products中,把iphoneos/iphonesimulator刪掉
接下來將原來的WKDownLoad中的podspec文件拷貝一份放到我們的WKDownloadLb項(xiàng)目中,用于進(jìn)行pod時(shí)的查找,并修改podspec,包括庫的名稱/homepage/source/,在初始化coding.net一個(gè)項(xiàng)目的時(shí)候,不要設(shè)置git.ignore,將coding.net中的homepage/source分別放到podspec對應(yīng)位置
為了外界不用改動引入我們.h文件的方式,我們做一層兼容,在framework外把所有的.h文件都加進(jìn)來(真實(shí)情況是將所有.h文件都加都一個(gè)文件中,只引用某一個(gè)文件就行),在根目錄下創(chuàng)建一個(gè)文件夾,把所有的.h文件都拷貝一份放到這里.
修改source_files/s.vendored_frameworks,其中vendored_frameworks其實(shí)是指明我們framework的地址專門存在的,由于已經(jīng)將framework放到了products中,所以地址更短了;source_files是除了framework之外的而外信息,就是我們的暴露header
修改完之后,按照
git add .
git status
git commit -m '標(biāo)記'
git remote
git remote add origin 地址
git push origin master
git tag -a 'tag值' -m '打標(biāo)簽'
git push --tags
pod lib lint? ?
(1)第一種錯(cuò)誤
發(fā)現(xiàn)好多小伙伴報(bào)這種錯(cuò)誤,
解決方案:
pod repo push 私有spec xxxxxx.podspec --allow-warnings --skip-import-validationpod spec lint xxxxxx.podspec --skip-import-validation
這個(gè)問題困擾了我很久,終于解決了,感謝小伙伴的貢獻(xiàn)啊.
(2)第二種錯(cuò)誤
是由于tag沒提交遠(yuǎn)端或者podspec中tag不一致導(dǎo)致的.
最終方案
我們現(xiàn)在遠(yuǎn)程倉庫有一個(gè)原生代碼組件和一個(gè)靜態(tài)庫組件,那么我們這樣有一個(gè)弊端,需要改兩個(gè)組件庫才能將兩個(gè)庫中代碼修改完畢,并且需要進(jìn)行pod文件名稱的修改,很不方便
能不能合并到一個(gè)里邊那?答案是肯定滴
再原來pod lib create 創(chuàng)建的私有庫demo中創(chuàng)建一個(gè)framework的復(fù)合工程
將framework復(fù)合工程中的部分中引入對原來類的引用,注意只是引用,并不拷貝,這樣就能保證修改的是一份代碼.
生成的framework復(fù)合工程部分,引用完之后代碼結(jié)構(gòu)
為了生成靜態(tài)庫,需要在復(fù)合項(xiàng)目的build Setting工程目錄下修改相應(yīng)的配置,release/mach-o/build avtive/修改暴露外界的.h到Public中等,然后編譯生成靜態(tài)framework庫,合并真機(jī)和模擬器的可執(zhí)行文件,放到原來組件的Classes的同級目錄下
?接下來,修改podspec文件,并設(shè)置靜態(tài)庫暴露在外的頭文件以及framework靜態(tài)庫
修改暴露的source_files為暴露classes中的.h,添加引用的framework地址,這樣就算設(shè)置完畢了.
接下來我們將修改后的組件上傳到原來未二進(jìn)制化時(shí)的私有庫中
進(jìn)行上傳...
在其他地方調(diào)用本組件,會發(fā)現(xiàn)已經(jīng)二進(jìn)制化了??,搞定
最終將上傳到私有庫的framework進(jìn)行pod install,但是報(bào)了一個(gè)錯(cuò)誤
發(fā)現(xiàn)是由于在pod file中打開了use_frameworks!,注掉就行
我們可以發(fā)現(xiàn):這樣并不需要添加一個(gè)新的pod依賴,并且通過pod update --no-repo-update就可以更新我們的遠(yuǎn)端私有庫,又節(jié)省了遠(yuǎn)端私有庫的資源
最后兼容源碼和靜態(tài)庫兩種類型
pod 中是可以設(shè)置環(huán)境變量的,比如 xxx pod install,xxx就是我們的環(huán)境變量
安裝的時(shí)候 IS_SOURCE=1 pod install,就會把原生代碼加進(jìn)來了
注意的地方:
1.如果我們生成的是.a的靜態(tài)庫,并不是framework靜態(tài)庫,那么需要將s.vendored_framework換成s.vendored_libraries就行了
2.如果原來是framework,再IS_SOURCE=1 pod install到原生,那么會發(fā)現(xiàn)安裝進(jìn)來的私有庫.m文件找不到了,這時(shí)需要先將之前的緩存清掉 pod cache clean --all,再將pods文件夾全部刪除,這個(gè)時(shí)候再執(zhí)行IS_SOURCE=1 pod install
3.如果我們的私有庫都用這個(gè)環(huán)境變量控制,就能起到統(tǒng)一管理私有庫源碼或者二進(jìn)制文件導(dǎo)入,但是這個(gè)時(shí)候有一個(gè)問題,如何只設(shè)置某一個(gè)為原生代碼?其他都為二進(jìn)制
可以給每一個(gè)私有庫單獨(dú)設(shè)置一個(gè)變量,表示當(dāng)前私有庫需要設(shè)置二進(jìn)制 - WKDownload
執(zhí)行WKDownload=1 pod install ,這樣,私有庫中的所有代碼都設(shè)置為二進(jìn)制形式,除了WKDownload為原生
4.私有庫中有依賴,那么怎么處理
不應(yīng)該包含依賴的xxx.a/framework文件
并且比如包含很多subspec,那么我們需要將所有的都放到一個(gè)framework中
快速生成framework
最后,為了方便誉尖,我們很多時(shí)候可以利用cocoapods-packager來快速完成framework打包罪既,但是必須注意,我們需要將原來的源碼已經(jīng)用上傳到托管平臺铡恕,并且已加了tag野崇。
1.安裝package
2.根據(jù)生成靜態(tài)庫
3.查看支持的類型
4.把不需要的類型刪除
根據(jù)路徑下的庫中的podspec文件问慎,生成相對應(yīng)的framework奖蔓,并且支持所有的機(jī)型以蕴,個(gè)人比較傾向于這個(gè)工具。
包含subspec的靜態(tài)庫的封裝
如果有framework里有依賴祭刚,那么我們不能用自動化的cocoapods packager打包framework框架里.
組合項(xiàng)目里創(chuàng)建target framework來進(jìn)行靜態(tài)庫的封裝,打包完之后其實(shí)是不包含我們的第三方依賴的,
我們的復(fù)合項(xiàng)目中想要用第三方的框架中的代碼,那么需要將我們創(chuàng)建的target放入pod中,根據(jù)pod 文件的修改
修改podspec? 去掉之前色subspec,將所有的代碼看成一個(gè)整體
修改podspec 文件里添加source_files/vendored_frameworks
去掉? 庫名稱.dependency? ? ? '依賴庫名稱'
然后按照framwork的封裝進(jìn)行提交到私有庫
歡迎關(guān)注我的公眾號,專注iOS開發(fā)涡驮、大前端開發(fā)暗甥、跨平臺技術(shù)分享。