寫在前面
所謂 SDK 開發(fā)戚绕,就是做一個庫(library)給別人用,本文重在介紹 iOS 平臺下的庫 -- framework
枝冀。
本文的結(jié)構(gòu)如下:
- 基本認識舞丛,這一部分介紹靜態(tài)耘子、靜態(tài)庫和framework的基本概念
- iOS 系統(tǒng)下的 framework 總結(jié),這一部分介紹 iOS 平臺下的庫
- 在 Xcode 中手動創(chuàng)建各類 framework
- 使用上面創(chuàng)建的 framework
本文使用的 Xcode 版本是 Version 10.1 (10B61)球切。
本文 demo 的 GitHub 地址在文章末尾谷誓!
1. 基本認識
1.1 什么是庫
首先來看什么是庫,庫(Library)說白了就是一段編譯好的二進制代碼吨凑,加上頭文件就可以供別人使用捍歪。
什么時候我們會用到庫呢?一種情況是某些代碼需要給別人使用鸵钝,但是我們不希望別人看到源碼糙臼,就需要以庫的形式進行封裝,只暴露出頭文件恩商。另外一種情況是变逃,對于某些不會進行大的改動的代碼,我們想減少編譯的時間怠堪,就可以把它打包成庫揽乱,因為庫是已經(jīng)編譯好的二進制了,編譯的時候只需要 Link 一下研叫,不會浪費編譯時間锤窑。
上面提到庫在使用的時候需要 Link,Link 的方式有兩種嚷炉,靜態(tài)和動態(tài)渊啰,于是便產(chǎn)生了靜態(tài)庫和動態(tài)庫。
我們從文件名可以直觀的分辨動態(tài)庫和靜態(tài)庫申屹。
一般來說绘证,動態(tài)庫以 .dylib
或者 .framework
后綴結(jié)尾;靜態(tài)庫以 .a
和 .framework
結(jié)尾哗讥。
1.2 動態(tài)庫嚷那、靜態(tài)庫和 framework 概念
嚴格意義上講,這三個概念不在一個維度上杆煞。framework 并不是庫魏宽,它只是一種打包方式,它既可以是動態(tài)庫也可以是靜態(tài)庫决乎。
靜態(tài)庫
靜態(tài)庫即靜態(tài)鏈接庫(Windows 下的 .lib队询,Linux 和 Mac 下的 .a)。之所以叫做靜態(tài)构诚,是因為靜態(tài)庫在編譯的時候會被直接拷貝一份蚌斩,復(fù)制到目標程序里,這段代碼在目標程序里就不會再改變了范嘱。
靜態(tài)庫的好處很明顯送膳,編譯完成之后员魏,庫文件實際上就沒有作用了。目標程序沒有外部依賴叠聋,直接就可以運行撕阎。當(dāng)然其缺點也很明顯,就是會使用目標程序的體積增大碌补。
動態(tài)庫
動態(tài)庫即動態(tài)鏈接庫(Windows 下的 .dll闻书,Linux 下的 .so,Mac 下的 .dylib/.tbd)脑慧。與靜態(tài)庫相反,動態(tài)庫在編譯時并不會被拷貝到目標程序中砰盐,目標程序中只會存儲指向動態(tài)庫的引用闷袒。等到程序運行時,動態(tài)庫才會被真正加載進來岩梳。
動態(tài)庫的優(yōu)點是囊骤,不需要拷貝到目標程序中,不會影響目標程序的體積冀值,而且同一份庫可以被多個程序使用(因為這個原因也物,動態(tài)庫也被稱作共享庫)。同時列疗,運行時才載入的特性滑蚯,也可以讓我們隨時對庫進行替換,而不需要重新編譯代碼抵栈。動態(tài)庫帶來的問題主要是告材,動態(tài)載入會帶來一部分性能損失,使用動態(tài)庫也會使得程序依賴于外部環(huán)境古劲。如果環(huán)境缺少動態(tài)庫或者庫的版本不正確斥赋,就會導(dǎo)致程序無法運行(Linux 下喜聞樂見的 lib not found 錯誤)。
iOS Framework
除了上面提到的 .a 和 .dylib/.tbd 之外产艾,Mac OS/iOS 平臺還可以使用 Framework疤剑。
Framework 實際上是一種打包方式,將庫的二進制文件闷堡,頭文件和有關(guān)的資源文件打包到一起隘膘,方便管理和分發(fā),和靜態(tài)庫動態(tài)庫的本質(zhì)是沒有什么關(guān)系缚窿。
在 iOS 8 之前棘幸,iOS 平臺不支持使用動態(tài) Framework,開發(fā)者可以使用的 Framework 只有蘋果自家的 UIKit.Framework倦零,F(xiàn)oundation.Framework 等误续。因為 iOS 應(yīng)用都是運行在沙盒當(dāng)中吨悍,不同的程序之間不能共享代碼,同時動態(tài)下載代碼又是被蘋果明令禁止的蹋嵌,沒辦法發(fā)揮出動態(tài)庫的優(yōu)勢育瓜,實際上動態(tài)庫也就沒有存在的必要了。
由于上面提到的限制栽烂,開發(fā)者想要在 iOS 平臺共享代碼躏仇,唯一的選擇就是打包成靜態(tài)庫 .a 文件,同時附上頭文件腺办。
iOS 8/Xcode 6 推出之后焰手,iOS 平臺添加了動態(tài)庫的支持,同時 Xcode 6 也原生自帶了 Framework 支持怀喉。
2. iOS 系統(tǒng)下的 framework 總結(jié)
有上面對庫
和framework
的基本認識之后书妻,本節(jié)對 iOS 平臺下的 framework 進行闡述。
2.1 framework 只是一種打包方式
framework 只是一種打包方式躬拢,它只是簡單的將二進制文件
躲履、頭文件
以及其它的一些信息聚合在一起。
本文后面聊闯,會手動制作 static framework 和 embedded framework工猜,這里,我們先從文件目錄結(jié)構(gòu)上菱蔬,研究一下這兩個 framework篷帅。
static framework 文件目錄結(jié)構(gòu)如下:
? StaticFramework.framework git:(master) tree
.
├── Headers
│ └── StaticFramework.h
├── Info.plist
├── Modules
│ └── module.modulemap
├── StaticFramework
└── _CodeSignature
├── CodeDirectory
├── CodeRequirements
├── CodeRequirements-1
├── CodeResources
└── CodeSignature
embedded framework 文件目錄結(jié)構(gòu)如下:
? EmbeddedFramework.framework git:(master) ? tree
.
├── EmbeddedFramework
├── Headers
│ └── EmbeddedFramework.h
├── Info.plist
├── Modules
│ └── module.modulemap
└── _CodeSignature
└── CodeResources
3 directories, 5 files
通過對比,我們發(fā)現(xiàn)二者并沒有太大區(qū)別拴泌,所以這也佐證了犹褒,framework只是一種打包方式,不代表庫的 link 特性
弛针。
2.2 iOS 系統(tǒng)下 framework 分類
從上面的描述可知叠骑,iOS 系統(tǒng)中的 framework 按照如下方式分類。
>>> Dynamic Framework
Dynamic Framework 削茁,動態(tài)庫宙枷,系統(tǒng)提供的 framework 都是動態(tài)庫,比如 UIKit.framework
茧跋,具有所有動態(tài)庫的特性慰丛。
>>> Static Framework
Static Framework,靜態(tài)庫瘾杭,用戶可以制作诅病,可以粗略的理解為,它等價于 頭文件 + 資源文件 + 二進制代碼
,它具有靜態(tài)庫的屬性贤笆。
>>> Embedded Framework
Embedded Framework蝇棉,這個是用戶可以制作的“動態(tài)庫”,它是受到 iOS 平臺限制(簽名機制
和沙盒機制
限制)的動態(tài)庫芥永,它具有部分動態(tài)特性
篡殷,比如:
- Embedded Framework 可以在
Extension可執(zhí)行文件
和APP可執(zhí)行文件
之間共享,但是不能像系統(tǒng)的動態(tài)庫一樣埋涧,在不同的 APP(進程) 中共享 - 系統(tǒng)的 Framework 不需要拷貝到目標程序中板辽,Embedded Framework 最后也還是要拷貝到 APP 中,下圖所示的是棘催,使用 Embedded Framework 的項目的 APP 目錄
注意:本質(zhì)上講劲弦,Embedded Framework 是動態(tài)庫,他只是我們給動態(tài)庫起的一個別名4及印F磕!
2.3 Embedded Framework 存在的意義
換言之纲仍,我們在哪些場景可以利用 Embedded Framework 的特性?贸毕?
這個我們在另外一篇文章中闡明郑叠。
接下來,手動創(chuàng)建一個 static framework 和 embedded framework明棍,來研究 framework 的特性乡革。
3. Xcode 中手動創(chuàng)建 framework
下圖是當(dāng)前版本 Xcode 提供的創(chuàng)建 framework 的模板。
本節(jié)將使用這兩個模板摊腋,分別創(chuàng)建一個動態(tài)的framework--DynamicFramework.framework
沸版,一個靜態(tài)的framework -- StaticFramework.framework
。
3.1 制作 embedded framework
使用 cocoa touch framework 模板創(chuàng)建一個名為 EmbeddedFramework 的項目兴蒸,
我們查看 Build Setting --> Linking --> Mach-O Type 视粮,這個選項默認是 Dynamic
,表示我們當(dāng)前為 embedded framework
橙凳。
為了方便測試蕾殴,給framework添加一個同名的類,并給這個類添加一個 log
實例方法岛啸。
實現(xiàn)如下:
@implementation EmbeddedFramework
- (void)log{
NSLog(@"Hello Embedded Framework!");
}
@end
3.2 制作 static framework
使用 cocoa touch framework 模板創(chuàng)建一個名為 StaticFramework 的項目钓觉,通過設(shè)置 Build Setting --> Linking --> Mach-O Type 為 Static Library
,將我們當(dāng)前的 framework 設(shè)置為 static framework坚踩。
同樣的荡灾,給 framework 添加一個同名的類,并給這個類添加一個log
實例方法,方法實現(xiàn)如下:
@implementation StaticFramework
- (void)log{
NSLog(@"Hello Static Framework!");
}
@end
4. 使用
新建一個 Single View App
批幌,在 APP里面測試我們上面創(chuàng)建的 framework础锐,這個項目命名為 FrameworkDemo。
為了測試方便逼裆,我們只在模擬器 iPhone XR 中測試郁稍,不在其它架構(gòu)平臺上測試。
4.1 static framework 的使用
生成 StaticFramework.framework
StaticFramework 項目中胜宇,我們 target 選擇模擬器 iPhone XR耀怜,然后編譯,編譯成功之后桐愉,在 Product 文件夾下面會出現(xiàn)一個 StaticFramework.framework财破。
使用 StaticFramework.framework
我們將這個 StaticFramework.framework 添加到 FrameworkDemo 這個項目中。
在 demo 工程中从诲,使用靜態(tài)framework左痢,調(diào)用代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
[[StaticFramework new] log];
}
控制臺打印結(jié)果如下:
2019-02-14 19:14:31.674758+0800 FrameworkDemo[39272:1382553] Hello Static Framework!
調(diào)用成功。
所以靜態(tài)庫的使用比較簡單系洛,直接將framework 添加到項目中即可俊性。
4.2 embedded framework 的使用
和 3.1 展示的過程一樣,我們將生成的 EmbeddedFramework.framework 添加到 demo 工程中描扯,并且調(diào)用定页。
#import "ViewController.h"
#import <StaticFramework/StaticFramework.h>
#import <EmbeddedFramework/EmbeddedFramework.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[StaticFramework new] log];
[[EmbeddedFramework new] log];
}
@end
運行 demo,程序crash了绽诚,控制臺輸出如下典徊。
dyld: Library not loaded: @rpath/EmbeddedFramework.framework/EmbeddedFramework
Referenced from: /Users/xieshoutan/Library/Developer/CoreSimulator/Devices/9124D297-42BC-467E-B343-59441AAA0FE0/data/Containers/Bundle/Application/3DAF0FC7-D040-478B-891A-AD678109380B/FrameworkDemo.app/FrameworkDemo
Reason: image not found
"dyld: Library not loaded"解決方案
需要在工程的 General
里的 Embedded Binaries
添加這個動態(tài)庫才能使用。
將 DynamicFramework.framework 添加進去恩够。
重新運行代碼卒落,程序正常運行。
2019-02-14 19:32:23.819682+0800 FrameworkDemo[39982:1415499] Hello Static Framework!
2019-02-14 19:32:23.819802+0800 FrameworkDemo[39982:1415499] Hello Dynamic Framework!
4.3 二者使用過程中的差異比較
Xcode 配置
前面已經(jīng)看出來了蜂桶,是用動態(tài)庫的時候儡毕,需要額外的在 Xcode 中進行動態(tài)庫配置。
ipa 包中表現(xiàn)
二者在 ipa 包中表現(xiàn)也不一致扑媚。
我們打開 FramewoDemo 的 ipa 包發(fā)現(xiàn):
- 動態(tài)庫單獨放在一個文件夾
Frameworks
中 - 靜態(tài)庫和源代碼一起妥曲,打成一個二進制文件
FrameworkDemo
如下是 FrameworkDemo.app
的文件目錄:
? FrameworkDemo.app tree
.
├── Base.lproj
│ ├── LaunchScreen.storyboardc
│ │ ├── 01J-lp-oVM-view-Ze5-6b-2t3.nib
│ │ ├── Info.plist
│ │ └── UIViewController-01J-lp-oVM.nib
│ └── Main.storyboardc
│ ├── BYZ-38-t0r-view-8bC-Xf-vdC.nib
│ ├── Info.plist
│ └── UIViewController-BYZ-38-t0r.nib
├── FrameworkDemo
├── Frameworks
│ └── EmbeddedFramework.framework
│ ├── EmbeddedFramework
│ ├── Info.plist
│ └── _CodeSignature
│ └── CodeResources
├── Info.plist
├── PkgInfo
└── _CodeSignature
└── CodeResources
7 directories, 13 files
4.4 動態(tài)庫和靜態(tài)庫的判斷 -- file 工具
在 4.3 中,我們可以通過查看 ipa 中的目錄結(jié)構(gòu)的方式钦购,來判斷靜態(tài)庫和動態(tài)庫檐盟。
這里我們可以使用 file
工具查看。
EmbeddedFramework.framework 的查看結(jié)果如下:
? EmbeddedFramework.framework git:(master) ? file EmbeddedFramework
EmbeddedFramework: Mach-O 64-bit dynamically linked shared library x86_64
StaticFramework.framework 的結(jié)果如下:
? StaticFramework.framework git:(master) ? file StaticFramework
StaticFramework: current ar archive random library
上面也可以看出押桃,兩個 framework 只支持 x86_64 架構(gòu)葵萎。
5. 總結(jié)
本文介紹了 iOS framework 相關(guān)的基礎(chǔ)知識,弄清楚了 iOS 中 framework 的種類、構(gòu)建方法和基本使用羡忘。
但事實上谎痢,我們實際開發(fā)中,我們并不是通過這種原始的方式來構(gòu)建 framework卷雕,現(xiàn)在比較通用的方案是基于cocopods來做的节猿。而且實際開發(fā)中,我們構(gòu)建的 framework 遠比這個復(fù)雜漫雕,包含資源文件的依賴滨嘱、其它靜態(tài)庫的依賴等等。
后文浸间,將介紹 cocopods 和 framework 的關(guān)系太雨。
參考文檔
iOS里的動態(tài)庫和靜態(tài)庫
iOS動態(tài)庫、靜態(tài)庫及使用場景魁蒜、方式
本文示例 demo github 地址