Comment on page
09.深入理解动态库与静态库
静态
和动态
是相对编 译时
和运行时
进行区分的静态库在编译时会被链接进MachO中
后缀一般为
.a
/.framework
在程序编译时不会被链接到MachO中,而是在程序启动的时候加载动态库到内存中。其中动态库分
动态链接库
和动态加载库
两种动态库文件一般为
.tbd(以前是.dylib)
/.framework
在编译阶段确定了依赖关系的动态库。当运行可执行文件时,如果尚未加载,则会将加载这些动态库。
常见的动态库都是这种
编译阶段不需要指定app需要依赖哪些动态库。当运行过程中需要加载某个动态库时,就会用
dlopen
函数动态的把库加载到内存中使用。这种玩玩可以,上架就别想了。比通过网络如下载一个动态库,然后加载调用。
一般来说,静态库直接拷贝进可执行文件,相较动态库对包体积的影响会大一些。
静态库被多个可执行文件依赖就会被拷贝多次。
因为动态库在应用启动的时候需要加载,所以对应用的启动速度是有一定的影响的。
严谨的说,对于已经加载过的动态库已经存在于共享缓存中,那么对启动速度的影响就很小了。
- 静态库
- 模块化,分工合作,提高了代码的复用及核心技术的保密程度
- 避免少量改动经常导致大量的重复编译连接
- 也可以重用,注意不是共享使用
- 动态库
- 可以将最终可执行文件体积缩小,将整个应用程序分模块,团队合作,进行分工,影响比较小
- 多个应用程序共享内存中得同一份库文件,节省资源
- 可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的
- 应用插件化
- 软件版本实时模块升级
- 在其它大部分平台上,动态库都可以用于不同应用间共享, 共享可执行文件,这就大大节省了内存
在iOS8
之前,苹果不允许第三方框架使用动态方式加载,从 iOS8 开始允许开发者有条件地创建和使用动态框架,这种框架叫做Cocoa Touch Framework
。虽然同样是动态框架,但是和系统framework
不同,苹果系统专属的framework
是共享的(如UIKit
),使用Cocoa Touch Framework
制作的动态库在打包和提交 app 时会被放到app main bundle
的根目录中,运行在沙盒
里,而不是系统中。也就是说,不同的 app 就算使用了同样的 framework,但还是会有多份的框架被分别签名、打包和加载。不过 iOS8 上开放了App Extension
功能,可以为一个应用创建插件,这样主 app 和插件之间共享动态库还是可行的。
Framework 是 Cocoa/Cocoa Touch 程序中使用的一种资源打包方式,可以将代码文件、头文件、资源文件、说明文档等集中在一起,方便开发者使用。一般如果是静态 Framework 的话,资源打包进 Framework 是读取不了的。静态 Framework 和 .a 文件都是编译进可执行文件里面的。只有动态 Framework 能在 .app 下面的 Framework 文件夹下看到,并读取 .framework 里的资源文件。
Cocoa/Cocoa Touch 开发框架本身提供了大量的 Framework,比如 Foundation.framework/UIKit.framework 等。需要注意的是,这些 Framework 无一例外都是动态库。
平时用的第三方 SDK 的 Framework 都是静态库,真正的动态库是上不了 AppStore(iOS8 之后能上 AppStore,因为 App Extension,需要动态库支持)。
表示暴露的头文件,一般都会有一个和 Framework 同名的 .h 文件,在创建 Framework 的时候文件夹里也会默认生成这样一个文件。有这个和 Framework 同名的 .h 文件 @import 导入库的时候编译器才能找到这个库。
主要就是这个 Framework 的一些配置信息。
这个文件夹里有个 module.modulemap 文件
framework module DynamicFramework {
umbrella header "DynamicFramework.h"
export *
module * { export * }
}
这里面有这样一句 umbrella header "DynamicFramework.h",umbrella 有保护伞、庇护的意思。
也就是说 Headers 中暴露的 DynamicFramework.h 文件被放在 umbrella 雨伞下保护起来了,所以我们需要将其他的所有需要暴露的 .h 文件放到 DynamicFramework.h 文件中保护起来,不然会出现警告。
@import 的时候也只能找到 umbrella 雨伞下保护起来的 .h 文件。
这个就是你源码编译而成的二进制文件,主要的执行代码就在这个里面。
如果我们在 Build Phases -> Copy Bundle Resources 里加入 .bundle 文件,那么创建出来的 .Framework 里就会有这个 .bundle 的资源文件夹。
CocoaPods
如何生成 Framework
的资源文件?我们能看到用
cocoapods
创建 Framework
的时候,Framework
里面有一个 .bundle
文件,跟 Framework
同级目录里也有一个 .bundle
文件。这两个文件其实是一样的。那这两个
.bundle
是怎么来的呢?我们能看到用 use_frameworks!
生成的 pod 里面,pods 这个 PROJECT 下面会为每一个 pod
生成一个 target。

1
那么如果这个
pod
有资源文件的话,就会有一个叫 xxx-bundleName
的 target
,最后这个 target
生成的就是 bundleName.bundle
。
2
在 xxx 的 target 的
Build Phases -> Copy Bundle Resources
里加入这个 .bundle,在 Framework 里面就会生成这样一个 bundle。在 xxx 的 target 的
Build Phases -> Target Dependencies
里加入这个target:xxx-bundleName
,就会在 Framework 的同级目录里生成这样一个 bundle。静态 Framework 里不需要加入资源文件。一般资源打包进静态 Framework 是读取不了的。
静态 Framework 和 .a 文件都是编译进可执行文件里面的。只有动态 Framework 能在 .app 的 Framework 文件夹下看到,并读取 .framework 里的资源文件。
你可以用
NSBundle * bundle = [[NSBundle mainBundle] bundlePath];
得到 .app
目录,如果是动态库你能在 Framework
目录下看到这个动态库以及动态库里面资源文件。然后你只要用
NSBundle * bundle = [NSBundle bundleForClass:<#ClassFromFramework#>];
得到这个动态库的路径就能读取到里面的资源了。但是如果是静态库的话,因为编译进了可执行文件里面,你也就没办法读到这个静态库了,你能看到 .app 下的 Framework 目录为空。报错 Reason: image not found
如果直接在工程里使用创建的动态库时候会出现此错误,需要在工程的 General 里的 Embedded Binaries 添加这个动态库才能使用。
因为创建的这个动态库其实也不能给其他程序使用的,而你的 App Extension 和 APP 之间是需要使用这个动态库的。这个动态库可以 App Extension 和 APP 之间共用一份(App 和 Extension 的 Bundle 是共享的),因此苹果又把这种 Framework 称为 Embedded Framework。
在使用
CocoaPods
的时候在 Podfile 里加入 use_frameworks!
,那么你在编译的时候就会默认帮你生成动态库,我们能看到每个源码 Pod 都会在 Pods 工程下面生成一个对应的动态库 Framework
的 target,我们能在这个
target
的 Build Settings -> Mach-O Type
看到默认设置是 Dynamic Library
,也就是会生成一个动态 Framework
,我们能在 Products
下面看到每一个 Pod 对应生成的动态库。这些生成的动态库将链接到主项目给主工程使用,但是我们上面说过动态库需要在主工程
target
的 General -> Embedded Binaries
中添加才能使用,而我们并没有在 Embedded Binaries
中看到这些动态库。那这是怎么回事呢,其实是 cocoapods
已经执行了脚本把这些动态库嵌入到了 .app
的 Framework
目录下,相当于在 Embedded Binaries
加入了这些动态库。我们能在主工程 target
的 Build Phase -> Embed Pods Frameworks
里看到执行的脚本。Last modified 2yr ago