簡(jiǎn)介
cocoapods在1.4.0推出了static framework,先扒扒歷史原因.
dymanic framework原因
在iOS8以前,蘋果只允許發(fā)布靜態(tài)庫(kù),當(dāng)然cocoapods只支持靜態(tài)庫(kù),但是在iOS8蘋果推出了APP extension的概念,可以對(duì)項(xiàng)目進(jìn)行擴(kuò)展,感興趣的可以看APP extension.
因?yàn)锳PP extension和主項(xiàng)目是兩個(gè)獨(dú)立的進(jìn)程,為了共享代碼,蘋果允許我們創(chuàng)建動(dòng)態(tài)庫(kù),即dynamic framework.
swift第三方庫(kù)
在swift語(yǔ)言日益優(yōu)化的前提下,我們想要進(jìn)行項(xiàng)目swift化,但是在Xcode 6.0 Beta 4的 Release Notes 中,可以找到這句話:
Xcode does not support building ``static libraries that include Swift code. (17181019)
動(dòng)態(tài)庫(kù)導(dǎo)致的static library報(bào)錯(cuò)
看了上面的原因,你會(huì)問pod直接使用動(dòng)態(tài)庫(kù)不就好了,但是對(duì)于pod來(lái)說,有這么幾個(gè)問題
包含靜態(tài)庫(kù)報(bào)錯(cuò)
The 'xxx' target has transitive dependencies that include static binaries動(dòng)態(tài)庫(kù)不能依賴靜態(tài)庫(kù)
ok,介紹完歷史原因,我們繼續(xù)看,在講解適配前,先了解幾個(gè)概念.
基礎(chǔ)介紹
在OC環(huán)境下,絕大多數(shù)項(xiàng)目都使用cocoapods進(jìn)行第三方庫(kù)的管理,pod可以實(shí)現(xiàn)依賴管理,版本控制等功能,對(duì)于主項(xiàng)目X依賴A,A內(nèi)部A->B,A->C,B→D,這類的依賴情況,主項(xiàng)目只需要引入A,在安裝時(shí)就會(huì)檢測(cè)其他的依賴pod是否存在,不存在進(jìn)行安裝.
pod的管理,使得項(xiàng)目中同一類的庫(kù)只存在一份,cocoapods的項(xiàng)目可以靜態(tài)庫(kù) 動(dòng)態(tài)庫(kù)二選其一,關(guān)于這兩種的區(qū)別下面會(huì)做詳細(xì)解釋
默認(rèn)使用靜態(tài)庫(kù)管理,如果想改為動(dòng)態(tài),需要在podfile內(nèi)部添加use_frameworks!字段,該字段告訴pod,使用框架的方式,安裝和管理第三方庫(kù)
靜態(tài)庫(kù)不能包含swift文件,pod將第三方編譯為static library,不能支持swift語(yǔ)言,新版的改為了framework的形式,下面介紹library和framework的區(qū)別.
library和framework
library僅能包含編譯后的代碼,即.a文件,不能包含其他的資源文件.
但是我們封裝的第三方庫(kù),有時(shí)需要包含.h文件,.nib文件,圖片,文檔扥g
framework可以包含以上所有類型.且支持包含swift代碼.
framework支持iOS8以后,而static library可以追溯到iOS6.
由于 iOS 的沙盒機(jī)制,自己創(chuàng)建的 Framework 和系統(tǒng)Framework 不同,App 中使用的 Framework 運(yùn)行在沙盒里,而不是系統(tǒng)中.每個(gè) App 都只能用自己對(duì)應(yīng)簽名的動(dòng)態(tài)庫(kù),做不到多個(gè) App 使用一個(gè)動(dòng)態(tài)庫(kù)
區(qū)別總結(jié)
動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)的區(qū)別如下
| 動(dòng)態(tài)庫(kù) | 靜態(tài)庫(kù) | |
|---|---|---|
| 命名空間 | 有單獨(dú)的命名空間,不同庫(kù)同名文件不會(huì)沖突 使用import<XXX/xxx.h>的方式引入 |
沒有單獨(dú)命名空間,同名文件沖突 引入方式import"xxx.h" |
| 加載時(shí)機(jī) | 在啟動(dòng)時(shí)加載,加載時(shí)間較長(zhǎng) | 構(gòu)建時(shí)加載 |
| 依賴關(guān)系 | 可以依賴動(dòng)態(tài)庫(kù),不能依賴靜態(tài)庫(kù) | 可以依賴動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù) |
| 是否能使用swift | 可以包含swift文件 | 在cocoapods1.4.0之后,可以使用use_framework!的方式包含swift文件 framework支持static_framework |
原理分析
上面的總結(jié)我們知道靜態(tài)庫(kù)在程序啟動(dòng)時(shí)被加載,動(dòng)態(tài)庫(kù)在使用時(shí)被加載
那么這些區(qū)別原理何在呢,下面分析下幾個(gè)概念:
編譯,目標(biāo)文件,符號(hào)表,鏈接
編譯: 編譯器生成機(jī)器代碼,生成目標(biāo)文件.
目標(biāo)文件包含兩種符號(hào)表: 1.文件轉(zhuǎn)換后的符號(hào)(名稱和方法的地址及偏移量) 2.未確定的符號(hào)(需要在鏈接階段才能解析完成的機(jī)器代碼)
目標(biāo)文件包含名為"main"的符號(hào),可以將代碼塊加載進(jìn)RAM運(yùn)行.并將"main"作為符號(hào)表的運(yùn)行入口的初始位置
鏈接: 將我們的各種目標(biāo)文件加上一些第三方庫(kù),和系統(tǒng)庫(kù)鏈接為可執(zhí)行文件
鏈接主要決議符號(hào),也就是變量函數(shù)等的地址
若符號(hào)來(lái)?靜態(tài)庫(kù)(本質(zhì)就是.o 的集合包)或 .o,將其納?鏈接產(chǎn)物,并確定符號(hào)地址
若符號(hào)來(lái)?動(dòng)態(tài)庫(kù),打個(gè)標(biāo)記,等啟動(dòng)的時(shí)候再說---交給 dyld 去加載和鏈接符號(hào)
于是鏈接加裝載就有了不同的情況
Load 裝載:將庫(kù)?件載?內(nèi)存
Static Loading:?jiǎn)?dòng)時(shí)
Dynamic Loading:?jiǎn)?dòng)后(使?時(shí))
Link 鏈接:決議符號(hào)地址Static Linking:構(gòu)建(鏈接)時(shí)
Dynamic Linking:運(yùn)?時(shí)(啟動(dòng)時(shí)或使?時(shí))
靜態(tài),共享和動(dòng)態(tài)庫(kù)
靜態(tài)庫(kù)只是目標(biāo)文件的集合.靜態(tài)庫(kù)只是為了方便處理大量文件.鏈接器只選取需要的文件并將它們寫入最終代碼塊,這使得靜態(tài)鏈接程序很大.( This makes statically linked programs pretty large.)
共享和動(dòng)態(tài)庫(kù)只需要被系統(tǒng)加載一次.然后使用該庫(kù)的工程只需要對(duì)其進(jìn)行引用即可.共享和動(dòng)態(tài)庫(kù)有兩種創(chuàng)建方式
1.全量的鏈接對(duì)象文件,包含大量的可被調(diào)用的符號(hào)表(真實(shí)的庫(kù)代碼)
2.通過"stub"對(duì)象文件,包含可調(diào)用方法的映射表(jump table)
通過動(dòng)態(tài)庫(kù)鏈接時(shí),stub對(duì)象文件是通過類似靜態(tài)庫(kù)的形式被加載到程序中的.但是方法只是加載了方法聲明.
當(dāng)程序使用動(dòng)態(tài)庫(kù)加載時(shí),系統(tǒng)需要額外鏈接存儲(chǔ)在RAM中的共享庫(kù).在加載系統(tǒng)共享庫(kù)的stub文件時(shí)有個(gè)實(shí)現(xiàn)技巧.有兩種方式可以實(shí)現(xiàn)加載系統(tǒng)共享庫(kù),1.系統(tǒng)攔截調(diào)用,進(jìn)入系統(tǒng),修改項(xiàng)目地址的上下文,轉(zhuǎn)換到共享庫(kù),工作量很大.另一種方式,將靜態(tài)庫(kù)映射到運(yùn)行程序通過虛擬內(nèi)存管理的地址空間,這使得共享庫(kù)對(duì)于多個(gè)項(xiàng)目來(lái)說,只是項(xiàng)目的一部分,雖然只在內(nèi)存中短暫存在.
這樣的話,代碼被共享,但是每個(gè)程序的堆棧由自己管理,使得各個(gè)程序員直接完全獨(dú)立.
結(jié)果
靜態(tài)庫(kù):穩(wěn)定,但是占用內(nèi)存空間.
動(dòng)態(tài)庫(kù):從系統(tǒng)加載代碼,共享代碼節(jié)約空間,但是可以會(huì)導(dǎo)致運(yùn)行時(shí)的錯(cuò)誤,且不易定位和修復(fù).
動(dòng)態(tài)庫(kù)詳解
蘋果官方關(guān)于動(dòng)態(tài)庫(kù)的描述:
動(dòng)態(tài)庫(kù)相比靜態(tài)庫(kù),減少了app可執(zhí)行文件的大小.并且可以只在使用時(shí),按需加載而不是在啟動(dòng)時(shí)加載.這個(gè)特性減低了啟動(dòng)時(shí)間,并且更優(yōu)秀的利用了內(nèi)存.
動(dòng)態(tài)庫(kù)不能依賴靜態(tài)庫(kù)
啟動(dòng)時(shí)間過長(zhǎng)解決辦法
-
第三方框架swift-staticlibs,集成的為動(dòng)態(tài)庫(kù),在構(gòu)建階段,轉(zhuǎn)為靜態(tài)庫(kù)加載的形式,這樣做的原因:
Xcode的static library不能包含swift
動(dòng)態(tài)庫(kù)啟動(dòng)時(shí)間過長(zhǎng)
使用static framework的方式,下面會(huì)做介紹
靜態(tài)庫(kù)詳解
我們可以在Build Setting里面通過Mach-O Type查看target的動(dòng)態(tài)或者靜態(tài)狀態(tài)
[圖片上傳失敗...(image-28b016-1519821296948)]
cocoapods1.4.0對(duì)于static framework的支持
static framework
pod在1.4.0之后提供了靜態(tài)框架的特性.過去的ues_framework!只能發(fā)布動(dòng)態(tài)庫(kù),現(xiàn)在可以發(fā)布靜態(tài)的框架.這一特性解決了過去動(dòng)態(tài)框架不能依賴靜態(tài)庫(kù)的弊端.現(xiàn)在的靜態(tài)framework也可以依賴靜態(tài)庫(kù),也可以依賴通過vendored_frameworks發(fā)布的第三方框架.
補(bǔ)充,vendored_frameworks和vendored_library是在podspec文件內(nèi)使用的屬性,用法是聲明包含的第三方framework和library.
背景
- static framework和library有什么區(qū)別呢? framework是對(duì)于library,頭文件和資源等內(nèi)容的封裝.library可以是動(dòng)態(tài)或者靜態(tài)的,靜態(tài)庫(kù)在構(gòu)建時(shí)期鏈接,但是動(dòng)態(tài)庫(kù)是在運(yùn)行時(shí)才進(jìn)行加載.
- 動(dòng)態(tài)庫(kù)不能依賴靜態(tài)庫(kù)是因?yàn)殪o態(tài)庫(kù)不需要在運(yùn)行時(shí)再次加載,如果多個(gè)動(dòng)態(tài)庫(kù)依賴同一個(gè)靜態(tài)庫(kù),會(huì)出現(xiàn)多個(gè)靜態(tài)庫(kù)的拷貝,而這些拷貝本身只是對(duì)于內(nèi)存空間的消耗.
- 另一個(gè)歷史原因是,過去很多庫(kù)是通過包含靜態(tài)庫(kù)的vendored_framework形式發(fā)布的.
- 在1.4.0之前,資源只能通過動(dòng)態(tài)庫(kù)的方式構(gòu)建,所以不能依賴vendored_framework的庫(kù).而且對(duì)于vendored_framework的二進(jìn)制庫(kù),無(wú)法在轉(zhuǎn)換成資源pod時(shí)仍保持動(dòng)態(tài)性
以上原因,使得pod在1.4.0提供了靜態(tài)框架的支持.用法簡(jiǎn)單,只需要在podspec文件內(nèi),聲明如下即可
s.static_framework = true
限制
所有swift庫(kù)需要保持一致的版本,包含Swift文件的framework,必須指定swift的版本號(hào),pod之后提供了新特性pod制定swift版本范圍
`Pod::Spec.``new` `do` `|s|`
`s.name = ``'BanannaLib'`
`s.version = ``'1.0.0'`
`s.swift_version = ``'>= 3.2'`
`s.source_files = ``'**/*.swift'`
`end`
支持包含swift文件
在創(chuàng)建私有pod時(shí),如果項(xiàng)目中包含swift文件,需要在podfile內(nèi)部添加use_framework!字段,如果不添加會(huì)報(bào)以下錯(cuò)誤
[!] Pods written in Swift can only be integrated as frameworks; add `use_frameworks!` to your Podfile or target to opt into using it. The Swift Pod being used is: erp-boss-common-ios
如果框架已經(jīng)聲明了static_framework = true,則可以包含swift文件,且可以依賴其他的靜態(tài)庫(kù).
蘋果官方的Xcode9發(fā)布文檔有以下說明Xcode release文檔
Xcode supports static library targets which contain Swift code. Debugging applications that use Swift static libraries may require a complete set of build artifacts that are in their original location. (``33297067``)`
Xcode支持包含swift代碼的靜態(tài)庫(kù)項(xiàng)目.
tips:
包含.a的static library,可以用lipo查看.a庫(kù)所支持的架構(gòu).
lipo -info libTestLib.a
Architectures in the fat file: libTestLib.a are: armv7 i386 x86_64 arm64
靜態(tài)庫(kù)報(bào)錯(cuò)
錯(cuò)誤
在我們使用use_frameworks!的時(shí)候,會(huì)遇到類似于下面的錯(cuò)誤提示,引起這種提示的原因,和各種情況,下面分析一下.
[!] The 'Pods-testDynamic_Example'` `target has transitive dependencies that include static binaries: (/Users/zhaoyanan/Documents/projects/pod/testDynamic/Example/Pods/SAKC-Ares/lib/libcares_iOS.a)
boss->A->B->SL(static library)
boss為主項(xiàng)目
SL為包含.a的靜態(tài)庫(kù)
boss->SL,沒有問題
在B內(nèi)增加static_framework = true, 可以解決boss->B->C問題
對(duì)于boss->A->B->C的情況,如果A,A',A''都依賴了B,需要保證他們依賴的方式相同,即都不指定版本號(hào),都在都指定特定的版本號(hào),或者都指定相同的范圍,聲明不同,則會(huì)報(bào)錯(cuò).
s.static_framework = true, s.subspec不需要再設(shè)置
其他方法
有一種做法,可以作為參考,沒有測(cè)試,對(duì)于A->B->C-SL的情況十是否使用也不可知,感興趣的可以研究下
A庫(kù)依賴B庫(kù)為例,B庫(kù)中有一個(gè)靜態(tài)庫(kù)libB.a :
在A庫(kù)中修改.podspec :
s.pod_target_xcconfig = {
'FRAMEWORK_SEARCH_PATHS'`=>'$(inherited) $(PODS_ROOT)/Crashlytics',
'OTHER_LDFLAGS' => '$(inherited) -undefined dynamic_lookup'
},
然后在Podfile中添加hook:
pre_install do |installer|
# workaround ``for` `https:``//github.com/CocoaPods/CocoaPods/issues/3289
def installer.verify_no_static_framework_transitive_dependencies; end
end
新特性
在pod1.5.0之后,安裝包含swift第三方庫(kù)的時(shí)候,不限制必須在podfile內(nèi)聲明use_frameworks!.但是,如果swift庫(kù)依賴OC庫(kù),就需要在OC庫(kù)內(nèi)允許modular headers
Modular Headers
CocoaPods在創(chuàng)建之初,就致力于封裝盡可能多的第三方庫(kù).pod管理了第三方庫(kù)的頭文件搜索路徑(header search paths).pod允許任意pod之間的相互引用,不需要考慮命名空間,不用制定import <nameSpace/fileName>.
例如B庫(kù)使用#import"A.h"的,pod會(huì)配置對(duì)應(yīng)的build setting來(lái)保證這種引入的可行.但是如果在其他庫(kù)內(nèi)增加了module maps,這種引用就會(huì)找不到文件.pod嘗試自動(dòng)去管理靜態(tài)庫(kù)的module maps,但是因?yàn)檫@樣破壞了pod的使用方式,沒有進(jìn)行下去.
說一下 module maps
在XCode的build setting內(nèi),Packaging內(nèi)有以下設(shè)置module map的選項(xiàng)
Defines Module (DEFINES_MODULE) :
如果設(shè)置為YES,會(huì)認(rèn)為項(xiàng)目自定義自己的組件,允許項(xiàng)目通過組件的方式引入Module Map File (MODULEMAP_FILE)
用來(lái)管理LLVM的module map,定義編譯器組件結(jié)構(gòu).如果defines module為YES的時(shí)候,如果Module Map File沒填,會(huì)自動(dòng)生成.
在pod1.5.0版本中,通過直接import和組件導(dǎo)入都能找到文件.對(duì)于pod開發(fā)者,可以在pod_target_xcconfig內(nèi)添加'DEFINES_MODULE' => 'YES'.對(duì)于使用者,可以在podfile內(nèi)添加use_modular_headers!允許直接import和module map.也可以通過:modular_headers => true配置特定的pod.
引用
- cocoapods 1.4.0特性匯總
- pod關(guān)于static framework的支持
- swift官方網(wǎng)站
- 動(dòng)態(tài)庫(kù)會(huì)導(dǎo)致啟動(dòng)時(shí)間太長(zhǎng)
- 動(dòng)態(tài)庫(kù)的個(gè)數(shù)不要多于6個(gè)
- library VS framework
- ios中的庫(kù)
- pod關(guān)于swift版本的討論
- swiftweekly,蘋果支持Xcode 9 beta 4發(fā)布swift
- pod制定swift版本范圍
- 靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)加載方式詳解
- 蘋果官方動(dòng)態(tài)庫(kù)文檔
- cocoapods原理總結(jié)
- dylib淺析
- 動(dòng)態(tài)庫(kù),在構(gòu)建階段,轉(zhuǎn)為靜態(tài)庫(kù)加載的形式
- 組件化-動(dòng)態(tài)庫(kù)
- pod issue static library
- import
- [pod 1.5.0 不用use_frameworks!]([http://blog.cocoapods.org/CocoaPods-1.5.0/]
- deep-dive-into-swift-frameworks