寫在前面
所謂 SDK 開發(fā),就是做一個庫(library)給別人用,本文重在介紹 iOS 平臺下的庫 -- framework。
本文的結構如下:
- 基本認識,這一部分介紹靜態(tài)、靜態(tài)庫和framework的基本概念
- iOS 系統(tǒng)下的 framework 總結,這一部分介紹 iOS 平臺下的庫
- 在 Xcode 中手動創(chuàng)建各類 framework
- 使用上面創(chuàng)建的 framework
本文使用的 Xcode 版本是 Version 10.1 (10B61)。
本文 demo 的 GitHub 地址在文章末尾!
1. 基本認識
1.1 什么是庫
首先來看什么是庫,庫(Library)說白了就是一段編譯好的二進制代碼,加上頭文件就可以供別人使用。
什么時候我們會用到庫呢?一種情況是某些代碼需要給別人使用,但是我們不希望別人看到源碼,就需要以庫的形式進行封裝,只暴露出頭文件。另外一種情況是,對于某些不會進行大的改動的代碼,我們想減少編譯的時間,就可以把它打包成庫,因為庫是已經編譯好的二進制了,編譯的時候只需要 Link 一下,不會浪費編譯時間。
上面提到庫在使用的時候需要 Link,Link 的方式有兩種,靜態(tài)和動態(tài),于是便產生了靜態(tài)庫和動態(tài)庫。
我們從文件名可以直觀的分辨動態(tài)庫和靜態(tài)庫。
一般來說,動態(tài)庫以 .dylib 或者 .framework 后綴結尾;靜態(tài)庫以 .a 和 .framework 結尾。
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)庫在編譯的時候會被直接拷貝一份,復制到目標程序里,這段代碼在目標程序里就不會再改變了。
靜態(tài)庫的好處很明顯,編譯完成之后,庫文件實際上就沒有作用了。目標程序沒有外部依賴,直接就可以運行。當然其缺點也很明顯,就是會使用目標程序的體積增大。
動態(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)庫或者庫的版本不正確,就會導致程序無法運行(Linux 下喜聞樂見的 lib not found 錯誤)。
iOS Framework
除了上面提到的 .a 和 .dylib/.tbd 之外,Mac OS/iOS 平臺還可以使用 Framework。
Framework 實際上是一種打包方式,將庫的二進制文件,頭文件和有關的資源文件打包到一起,方便管理和分發(fā),和靜態(tài)庫動態(tài)庫的本質是沒有什么關系。
在 iOS 8 之前,iOS 平臺不支持使用動態(tài) Framework,開發(fā)者可以使用的 Framework 只有蘋果自家的 UIKit.Framework,F(xiàn)oundation.Framework 等。因為 iOS 應用都是運行在沙盒當中,不同的程序之間不能共享代碼,同時動態(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 總結
有上面對庫和framework的基本認識之后,本節(jié)對 iOS 平臺下的 framework 進行闡述。
2.1 framework 只是一種打包方式
framework 只是一種打包方式,它只是簡單的將二進制文件、頭文件 以及其它的一些信息聚合在一起。
本文后面,會手動制作 static framework 和 embedded framework,這里,我們先從文件目錄結構上,研究一下這兩個 framework。
static framework 文件目錄結構如下:
? StaticFramework.framework git:(master) tree
.
├── Headers
│ └── StaticFramework.h
├── Info.plist
├── Modules
│ └── module.modulemap
├── StaticFramework
└── _CodeSignature
├── CodeDirectory
├── CodeRequirements
├── CodeRequirements-1
├── CodeResources
└── CodeSignature
embedded framework 文件目錄結構如下:
? 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 目錄

注意:本質上講,Embedded Framework 是動態(tài)庫,他只是我們給動態(tài)庫起的一個別名?。?!
2.3 Embedded Framework 存在的意義
換言之,我們在哪些場景可以利用 Embedded Framework 的特性??
這個我們在另外一篇文章中闡明。
接下來,手動創(chuàng)建一個 static framework 和 embedded framework,來研究 framework 的特性。
3. Xcode 中手動創(chuàng)建 framework
下圖是當前版本 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,表示我們當前為 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 的項目,通過設置 Build Setting --> Linking --> Mach-O Type 為 Static Library,將我們當前的 framework 設置為 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 中測試,不在其它架構平臺上測試。
4.1 static framework 的使用
生成 StaticFramework.framework
StaticFramework 項目中,我們 target 選擇模擬器 iPhone XR,然后編譯,編譯成功之后,在 Product 文件夾下面會出現(xiàn)一個 StaticFramework.framework。
使用 StaticFramework.framework

我們將這個 StaticFramework.framework 添加到 FrameworkDemo 這個項目中。

在 demo 工程中,使用靜態(tài)framework,調用代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
[[StaticFramework new] log];
}
控制臺打印結果如下:
2019-02-14 19:14:31.674758+0800 FrameworkDemo[39272:1382553] Hello Static Framework!
調用成功。
所以靜態(tài)庫的使用比較簡單,直接將framework 添加到項目中即可。
4.2 embedded framework 的使用
和 3.1 展示的過程一樣,我們將生成的 EmbeddedFramework.framework 添加到 demo 工程中,并且調用。
#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 配置
前面已經看出來了,是用動態(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 中的目錄結構的方式,來判斷靜態(tài)庫和動態(tài)庫。
這里我們可以使用 file 工具查看。
EmbeddedFramework.framework 的查看結果如下:
? EmbeddedFramework.framework git:(master) ? file EmbeddedFramework
EmbeddedFramework: Mach-O 64-bit dynamically linked shared library x86_64
StaticFramework.framework 的結果如下:
? StaticFramework.framework git:(master) ? file StaticFramework
StaticFramework: current ar archive random library
上面也可以看出,兩個 framework 只支持 x86_64 架構。
5. 總結
本文介紹了 iOS framework 相關的基礎知識,弄清楚了 iOS 中 framework 的種類、構建方法和基本使用。
但事實上,我們實際開發(fā)中,我們并不是通過這種原始的方式來構建 framework,現(xiàn)在比較通用的方案是基于cocopods來做的。而且實際開發(fā)中,我們構建的 framework 遠比這個復雜,包含資源文件的依賴、其它靜態(tài)庫的依賴等等。
后文,將介紹 cocopods 和 framework 的關系。
參考文檔
iOS里的動態(tài)庫和靜態(tài)庫
iOS動態(tài)庫、靜態(tài)庫及使用場景、方式
本文示例 demo github 地址