iOS 动态库与静态库基础

在iOS开发中,将特定功能代码封装在一个库中,对外提供接口调用,这样方便维护和集成,如网络库。库有静态库和动态库,我们在集成时该选择哪种?制作自己的库时,该如何指定?

一、问题引出

在使用CocoaPods管理三方库时,Podfile文件中关于use_frameworks!的使用有以下特点:

说明:s.static_framework = true\false用于在库的podspec文件中声明是否要生成静态库。

Podfile选项 \ Podspec s.static_framework = true s.static_framework = false
use_frameworks! 静态库 动态库
不使用use_frameworks! 静态库,swift项目导入库时报错,OC不会 静态库,swift项目导入库报错,OC不会
use_frameworks! :linkage => :static 静态库 静态库
use_frameworks! :linkage => :dynamic 静态库 动态库

结论:

1.swift项目需要使用use_frameworks!选项,后面可以接:linkage => :static等。
2.库通过s.static_framework = true指定了生成静态库,那么集成时就会是静态库。
3.库没有通过s.static_framework = true指定静态库,那么集成时可以通过上述表格中方式控制。

现在我们知道了如何通过pod控制集成动、静态库,那么动态库、静态库究竟有啥区别?

二、动、静态库简单对比

对比项 静态库 动态库
文件格式 .aframeworkxcframework .tbd.dylibframeworkxcframework
编译链接 链接时合并到可执行文件中;推送扩展等插件依赖了该库也会拷贝一份 编译链接时不合并,会独立生成一个动态库类型的Mach-O文件,放在xxx.app/Frameworks/xxx.framework中;其它扩展、插件依赖了不会再生成一份
对启动影响 内容跟随主二进制加载到内存,对启动影响较小 启动时dyld会加载其Mach-O并进行符号解析,相比静态库更耗时
包体积 如果主工程依赖该库而扩展插件未依赖,那么包体积会相比做成动态库小一点 相比静态库要大一丁点

三、了解静态库

Static libraries are collections or archives of object files.即静态库是.o文件的集合或归档。

1. iOS工程中的静态库

静态库主要文件形式是.a文件,以及静态库类型的framwork。使用CocoaPos集成的库,如果在Podfile中写了:

install! 'cocoapods',
# 禁用输入输出路径,不在生成的 Xcode 项目中包含特定的输入输出路径,从而避免一些可能的兼容性问题。
  disable_input_output_paths: true, 
  generate_multiple_pod_projects: true # 让每个pod依赖库成为一个单独的项目引入,这样大大提升了编译速度

那么每个pod install后每个库会对应一个工程,可以通过在对应工程的build setting中查看Mach-O type确定静态库还是动态库。
关于Mach-O基本了解可以查看:iOS中Mach-O概览

2. framework静态库和.a的区别

》.a是一个纯二进制文件,.framework中有二进制文件、头文件、资源文件、模块文件Modules。
》常规开发时,.a文件不能直接使用,至少要有.h文件配合,.framework文件可以直接使用。
》.a + .h + sourceFile + Modules = .framework。

纯swift生成静态库时,如果生成.a静态库无法直接使用,建议生成framework静态库。(swift语言生成.a文件后,我们拖入项目中使用会发现import模块会报错:No such module 'XXX'

3. 制作静态库

3.1手动制作一个静态库

你自己根据网上资料的方法操作生成,但如果给其他库中定义的类增加OC分类时,需要注意是否需要增加-Objc编译标识。

3.2 通过cocoapods

方法一:
在库的podspec文件中声明生成静态库,这样在pod install之后,xcode最终会编译为静态库。

# 在库的podspec文件中写如下设置
s.static_framework = true

方法二:
在Podfile中集成库时声明:

use_frameworks! :linkage => :static
或者另一个方法在pre_install中进行hook设置static_framework

4. 静态库framework里面的构成

我们打开手动制作的静态库framework,里面的文件有:

文件夹 内容
framework静态库
framework静态库.png
_CodeSignature:签名相关;Headers:公开的头文件;Modules:模块头;ZLLibaTest是二进制文件
Modules
Modules文件详情.png

5. Modules

苹果推出Modules主要是为了支持模块化编程,提供更清晰的代码组织和更好的命名空间管理。Modules文件夹里放着.modulemap.swiftmodule文件。.modulemap是用于C\OC,.swiftmodule用于swift。

有了Modules后,对于OC库、Swift库、OC+Swift混编的库,外部使用时都可以导入模块,不需要导入头文件,使得开发更加方便。

@import WebKit.WebKitLegacy; //in Objective-C
import WebKit.WebKitLegacy   //in Swift

细节可以阅读:开发进阶-Module与Swift库

6. 制作静态库注意点

6.1 静态库符号冲突

符号冲突指的是在OC/C的静态库中,全局变量、静态变量或函数名如果与应用程序或其他静态库中的全局符号相同,导致冲突发生符号重复定义错误. (亲测,如果多个静态库中有相同名称的OC分类,不会导致符号冲突,对于相同名称分类中相同方法的调用,最终调用的方法实现是较晚编译的库中方法。)

使用Swift库一般不会产生符号冲突,Swift 引入了模块化编程的概念,每个 模块拥有自己的命名空间,不同模块中相同名称的符号不会发生冲突。

6.2 静态库中OC分类中的方法找不到运行时奔溃
场景

假设一个静态库,库中有OC写的分类,但分类所属的类定义不在库中如NSString
在把这个静态库集成到工程里后,如果编译设置other linker flags没有添加-ObjC,那么在使用这个OC分类的方法时,就会在运行时奔溃: unrecognized selector sent to class ..

原因

由于标准UNIX静态库的实现、链接器和Objective-C的动态特性之间的问题,出现了“选择器未识别”运行时异常。Objective-C并没有为每个函数(或方法,在Objective-C中)定义链接器符号,而是只为每个类生成链接器符号。如果使用类别扩展预先存在的类,则链接器不知道如何将核心类实现的对象代码与类别实现相关联。这样可以防止在生成的应用程序中创建的对象响应类别中定义的选择器。

这也就是说,静态库中的OC分类的方法没有与类关联起来。

改法

如果是手动集成这种静态库,需要在主工程中的other linker flags添加-ObjC

-ObjC参数的作用:
This flag causes the linker to load every object file in the library that defines an Objective-C class or category. While this option will typically result in a larger executable (due to additional object code loaded into the application), it will allow the successful creation of effective Objective-C static libraries that contain categories on existing classes.
此标志使链接器加载库中定义Objective-C类或类别的每个对象文件。虽然此选项通常会导致更大的可执行文件(由于应用程序中加载了额外的对象代码),但它将允许成功创建有效的Objective-C静态库,其中包含现有类的类别。

有兴趣可以了解具体原因 iOS静态库中类的分类问题和符号冲突问题

另外,如果使用的库是动态库,也不会有这个问题,原因是动态库是运行时才链接的。

动态库是在运行时被动态加载到内存中的,而不是在编译时被静态链接。这意味着在动态库加载时,它的所有代码才会被加入到进程的地址空间中。相比之下,静态库在编译时就会被链接到可执行文件中,可能会引起加载的时机问题。

CocoaPods集成静态库没有这个问题

如果是使用CocoaPods集成静态库,那么会自动给工程添加编译参数-ObjC, 所以使用CocoaPods集成的静态库中的OC分类不会有这个问题。

Pods-工程名.debug.xcconfig中
OTHER_LDFLAGS = $(inherited) -ObjC -framework "AFNetworking"

四、了解动态库

1. iOS中的动态库

系统提供的framework都是动态库类型,比如UIKit.frameworklibc++.tbd;在集成库时,如果是静态库类型,那么静态库内容最终是在主二进制中;如果是动态库,会放在ipa中的Frameworks目录。

iOS 动态库.png

如果是cocoapods集成库,那么如果在库的spec中没有指定s.static_framework = true时,在podfile以下写法都是生成动态库:

方法一:
 use_frameworks!

方法二:
 use_frameworks! :linkage => :dynamic #使用动态链接

2. 动态库的格式介绍

.tbd(Text-Based Stub Library)
.tbd 文件是一种文本格式的库文件描述符,主要包含了库的元数据信息,如符号列表、版本信息等。
在 iOS 中,.tbd 文件通常用于描述系统框架和库,例如 iOS SDK 提供的框架。
这些文件并不包含实际的二进制代码,而是提供了一个轻量级的描述,用于编译和链接时确定库的接口和依赖关系。
在 Xcode 构建过程中,.tbd 文件会被用于生成实际的动态库链接信息。
.dylib
.dylib 文件是实际的动态库文件,包含了编译好的二进制代码、数据等。
在 iOS 中,.dylib 文件用于存储动态链接库的实现,可以由系统或第三方提供。
这些文件是真正的共享库,运行时动态加载到应用程序中,提供所需的功能。
.framework
目录结构是规范化,是一种更为结构化的库格式,用于更方便地组织和使用共享代码和资源。
XCFramework
引入了对多平台和多架构的支持,可以包含适用于不同平台和处理器架构的二进制版本。类似于胖二进制。

系统动态库

各种格式都有。从 Xcode7 在导入系统动态库时,可以发现.dylib文件变成了.tbd文件。.tbd文件相比.dylib文件来说包大小更小,实际使用的还是dylib的二进制代码库。
stackoverflow的回答
比如: libsqlite3.tbd是个文本文件,其安装名是libsqlite3.dylib.

archs: [ armv7, armv7s, arm64 ]
platform: ios
install-name: /usr/lib/libsqlite3.dylib
current-version: 216.4
compatibility-version: 9.0
exports:

  • archs: [ armv7, armv7s, arm64 ]
    symbols: [ __sqlite3_lockstate, __sqlite3_purgeEligiblePagerCacheMemory,
    __sqlite3_system_busy_handler, __sqlite_auto_profile,
    ...

So it appears that the .dylib file is the actual library of binary code that your project is using and is located in the /usr/lib/ directory on the user's device. The .tbd file, on the other hand, is just a text file that is included in your project and serves as a link to the required .dylib binary. Since this text file is much smaller than the binary library, it makes the SDK's download size smaller.

二方库

我们创建的动态库一般是framework和xcframework格式。

3. 动态库的链接

Static frameworks are linked at compile time. Dynamic frameworks are linked at runtime

库类型 链接时期截图
静态库
静态库.png
动态库
动态库.png

3. 动态库的优缺点

系统的动态库是各个APP可以共享一份的,这样能节省内存。自己生成的动态库仅限于自己APP内部共享,即和扩展、插件等进程共享。

动态库也可支持设置启动时不加载,在实际用到的时使用dlopen加载;
https://www.jianshu.com/p/08b0cb296278
缺点:
动态库相比做成静态库,最终的ipa包体积会更大一点点;启动时,动态库需要独立加载并动态链接符号,所以启动耗时多。

五、XCFramework

XCFramework 是苹果新出的库类型,在 Xcode 11 及 cocoapods 1.9 以上版本被支持,与普通动态库/静态库最大的区别是将多个平台(iOS, macOS, tvOS, watchOS, iPadOS, carPlayOS,模拟器)的二进制库,捆绑到一个可分发的.xcframework捆绑包中,支持所有的苹果平台和架构。
对比使用 .framework 格式,使用 .xcframework 格式 APP 包大小和启动速度都有提升。

相关资料:
苹果关于Mach-O的说明文档:
https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html#//apple_ref/doc/uid/TP40001827-SW1
苹果关于动态库的说明文档:
https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/OverviewOfDynamicLibraries.html#//apple_ref/doc/uid/TP40001873-SW1
苹果关于静态库OC分类时的文档:
Building Objective-C static libraries with categories:
网友总结文档:
XCFramework 基础-用脚本生成
iOS静态库中类的分类问题和符号冲突问题
iOS之深入解析静态库和动态库

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容