iOS静态库和动态库详解

3,394 阅读6分钟

库(Library)

是一段编译好的二进制代码,加上头文件就可以直接链接(Link)使用了

库的种类有两种: 静态库和动态库

静态库

静态库即静态链接库

  • 一组目标文件(object file, 以 .o 为后缀)的集合, 即很多目标文件经过压缩打包后形成的文件
  • 格式
    • Windows 的 .lib
    • Linux 和 iOS 下的 .a
    • iOS独有的.framework。
  • 优势
    • 提供的是目标文件, 所以不需要重新编译, 只需要链接即可
    • 加载 App 速度更快, 因为在编译时已经进行了链接, 启动时不需要进行二次查找启动
  • 缺点:
    • 浪费内存和磁盘空间,模块更新困难

动态库

动态链接库, Dynamic Libraries, 也称作 Shared Library

  • 动态库并不会在编译时编译到app中, app只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。
  • 根据载入时间 (load time)将动态库分为两种:
    • 动态链接库: 通过dyld程序在app启动时将加载动态库
    • 动态加载库: app启动后再使用dlopen来懒加载动态库(iOS不支持, MacOS支持)
  • 格式
    • MacOS/iOS: .tbd, .dylib 或封装成 .framework, xcframework
    • Linux: .so
    • Windows: .dll
  • 优势 动态库不需要在编译时置入 app 中, 因此理论上app体积会更小, 而且可以做到动态库内容不需要重新编译即可获得最新功能
  • 缺点:

会导致一些性能损失。但是可以优化,比如延迟绑定(Lazy Binding)技术

  • 可以前往MacOS系统路径 /usr/lib 文件夹查看系统的动态库.

image.png

Framework

  • 一种打包方式,其实就是文件夹, 将库的二进制文件,头文件和有关的资源文件(图片, Xibs, 动态库, 静态库, 文档)打包到一起,方便管理和分发。

组成

  • Headers: 包含了 Framework 对外公开的头文件, Swift 并不会用到这些 Headers

  • swiftmodule:是编译器对代码分析之后,将可暴露的类或方法写在其中, 相当于Swift库的头文件. 是二进制且内容随时会改变

image.png

  • executable(静态库/动态库)
  • bundle 文件/图片
  • xibs
  • ...

Embedded Framework

  • Framework 和系统的 UIKit.Framework 还是有很大区别。系统的 Framework 不需要拷⻉到目标程序中,我们自己做出来的 Framework 哪怕是动态的,最后也还是要拷⻉到 App 中,因此把这种 Framework 称为 Embedded Framework
  • 开发中使用的动态库会被放入到ipa下的framework目录下,基于沙盒运行。
  • 不同的App使用相同的动态库,并不会只在系统中存在一份。而是会在多个App中各自打包、签名、加载一份。

动静态库的混用

我们可以在一个项目中使用一部分动态库, 再使用一部分静态库, 如果涉及到第三方库与库之间的依赖关系时, 那么遵守如下原则:

  • 静态库可以依赖静态库
  • 动态库可以依赖动态库
  • 静态库可以依赖动态库
  • 动态库不能依赖静态库! 动态库不能依赖静态库是因为静态库不需要在运行时再次加载, 如果多个动态库依赖同一个静态库, 会出现多个静态库的拷贝, 而这些拷贝本身只是对于内存空间的消耗

如果项目中有一个库必须是静态库时, 那么其整个依赖链路上的所有库都必须以静态库被引入, 如下图:

image.png 在 库 4 为静态库的情况下, 整个依赖链路上的所有库(库 5 与库 3)都必须以静态库形式被项目依赖

tdb

  • tbd全称是text-based stub libraries,本质上就是一个YAML描述的文本文 件。
  • 他的作用是用于记录动态库的一些信息,包括导出的符号、动态库的架构信息、动 态库的依赖信息
  • 用于避免在真机开发过程中直接使用传统的dylib。
  • 对于真机来说,由于动态库都是在设备上,在Xcode上使用基于tbd格式的伪 framework可以大大减少Xcode的大小。

XCFramework

是苹果官方推荐的、支持的,可以更方便的表示一个多个平台和架构的分发二进制库的格式。 需要Xcode11以上支持。 是为更好的支持Mac Catalyst和ARM芯片的macOS。 专⻔在2019年提出的framework的另一种先进格式。

和传统的framework相比:

  1. 可以用单个.xcframework文件提供多个平台的分发二进制文件;
  2. 与Fat Header相比,可以按照平台划分,可以包含相同架构的不同平台的文件;
  3. 在使用时,不需要再通过脚本去剥离不需要的架构体系。

创建动态库静态库

命令

  • 创建静态库 libtool -static
  • 创建动态库 libtool -dynamiclib

XCode

创建 framework和静态库

image.png 创建完成后, 选中Mach-O Type, 调整Dynamic LibraryStatic Library

image.png

Cocoaspods 库配置

  • Cocoapods podfile 默认是构造静态库
  • use_frameworks! 选项让我们可以以, .framework 和动态库形式引入
  • s.static_frameworks = true 指定本库作为静态库引入.
platform :ios, '11.0'
source 'https://cdn.cocoapods.org/'
use_frameworks!

target 'Test' do
  pod 'AFNetworking'
end

为什么动态库不能动态加载

在 iOS app 启动时系统会查找我们所依赖的所有动态库并加载, 这降低了我们 App 的启动速度, 那么可不可以将动态库的调用时间延迟到 app 运行时? 答案是不能!

不能动态加载动态库的原因是iOS系统的限制. 查看苹果的 API 文档, 会发现有一个方法提供了加载可执行文件的功能, 那就是 NSBundle 的 load 方法 (底层实现为 dlopen 函数), 如下所示:

image.png

  • iOS 出于安全考虑, 在加载可执行代码前, 需要校验签名.
  • load 方法的内部实现是调用了 dlopen, 而真机的 dlopen 内部还会调用dlopen_preflight 先校验签名. 如果库不是事先打包进 app(打包进 app 的话会与 app 有相同签名), 就会报签名错误(Signature error), 从而加载不成功.
  • 因此动态加载库在模拟器上可以实现, 但是真机上不能运行
  • 所有目前只能在 Mac OS 上使用. 如果以后系统放开签名校验, 那么 iOS 中也就可以动态加载了

参考文章

iOS 中的静态库与动态库 - 知乎 (zhihu.com)