静态库/动态库

要研究这个问题,首先我们需要知道这个问题:什么是库?
在我们的日常开发中经常会用到别人封装好的第三方库,比如AFNetworking,SDWebImage等,而对于一些可以被抽取为单独功能组件的逻辑代码,我们也会将部分代码封装成库,以便使用。那么,库到底是什么呢?
库实际上是共享程序代码的方式,一般分为静态库和动态库。那么两者又有什么区别呢?

静态库与动态库的区别

静态库

静态库的后缀为.a,链接时完整地拷贝至可执行文件中,多个程序均使用某一静态库,那么就有多份冗余拷贝

动态库

动态库的后缀为.dll(windows) .dylib(mac) so(linux).链接时不复制,在程序启动后用 dyld 加载,然后再决议符号.所以理论上动态库只用存在一份,多个程序都可以动态链接到这个动态库上面,达到了节省内存的目的(不是磁盘是内存中只有一份动态库),还有另外一个好处,由于动态库并不绑定到可执行程序上,所以我们想升级这个动态库就很容易

tip

tip1 .framework为什么既是静态库又是动态库
系统的.framework是动态库,我们自己建立的.framework是静态库

tip 2 .a与.framework有什么区别?
.a是一个纯二进制文件,.framework中除了有二进制文件之外还有资源文件。
.a文件不能直接使用,至少要有.h文件配合,.framework文件可以直接使用。
.a + .h + sourceFile = .framework

静态库和动态库的依赖关系

在探讨这个问题之前,我们先来了解一下编译和链接
编译: 将我们的源代码文件编译为目标文件
链接: 将我们的各种目标文件加上一些第三方库,和系统库链接为可执行文件。
由于某个目标文件的符号(可以理解为变量,函数等)可能来自其他目标文件,其实链接这一步最主要的操作就是 决议符号的地址。

若符号来⾃静态库(本质就是.o 的集合包)或 .o,将其纳⼊链接并确定符号地址
若符号来⾃动态库,打个标记,等启动的时候再说---交给 dyld 去加载和链接符号。

静态库和动态库的依赖关系有以下四种:

1.libA.a dependency libB.a

静态库互相依赖,这种情况非常常见,制作静态库的时候只需要有被依赖的静态库头文件在就能编译出来。但是这就意味者你需要告诉使用者你的依赖关系

幸运的是 CocoaPod就是这样做的

2.UIKit.dylib dependency Foundation.dylib

动态库依赖动态库,两个动态库是相互隔离的具有隔离性,但是制作静态库的时候需要被依赖动态库参与链接,但是具体的符号决议交给dyld来做。

3.libA.a dependency Foundation.dylib

静态库依赖动态库,也很常见,静态库制作的时候也需要动态库参与链接,但是符号的决议交给 dyld 来做。

4.MyXX.dylib dependency libA.a

动态库依赖静态库,这种情况就有点特殊了。首先我们设想动态库编译的时候需要静态库参与编译,但是静态库交由 dyld 来做符号决议,这和我们前面说的就矛盾了啊。静态库本质是一堆.o 的打包体,首先并不是二进制可执行文件,再者你无法保证主程序把静态库参与链接共同生成二进制可执行文件。
目前的编译器的解决办法是,首先我无法保证主程序是否包含静态库,再者静态库也无法被dyld加载,那么我直接把你静态库的.o 偷过来,共同组成一个新的二进制。也被称做吸附性

那么我有多份动态库都依赖同样的静态库,每个动态库为了保证自己的正确性会把静态库吸附进来。然后两个库包含了同样的静态库,于是问题就出现了。

总结
可执⾏⽂件(主程序或者动态库)在构建的链接阶段

遇到静态库,吸附进来
遇到动态库,打标记,彼此保持独⽴

如何生成静态/动态库的包

1. 创建项目

如下图使用xcode新建动态/静态包即可:


create.png

2. 设置参数

2.1 Build Setting ->Enable Bitcode

针对你所要创建的库对应修改Enable Bitcode的值,具体参加以下的解释:

官方: Bitcode is an intermediate representation of a compiled program. apps you upload to App Store Connect that contain bitcode will be compiled and linked on the App Store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the App Store. For iOS apps, bitcode is the default, but optional. For watchOS and tvOS apps, bitcode is required. If you provide bitcode, all apps and frameworks in the app bundle (all targets in the project) need to include bitcode.

翻译: Bitcode是编译程序的中间表现,上传到app store connect的包含bitcode的应用程序将在app store上编译和链接。包含Bitcode可以在不提交新版本App的情况下,允许Apple在将来的时候再次优化你的App 二进制文件。 对于iOS Apps,Bitcode默认为yes,但是可选的(可以改为NO)。对于WatchOS和tvOS,bitcode是强制的。如果你的App支持bitcode,App Bundle(项目中所有的target)中的所有的Apps和frameworks都需要包含Bitcode。

2.2 Build Settings ->Build Active Architecture Only

Architecture.png

DebugRelease属性设置为YES时,是为了debug的时候编译速度更快,它只编译当前的architecture版本,建议打包时进行关闭

2.3 设置外部可访问文件

public.png

3. 打包

使用xcodebuild命令打包,并使用lipo命令合并并验证

可以手动打包,也可以使用脚本打包

// 切换到项目目录
cd ~/Desktop/TestFrameworkDemo

// 使用xcodebuild命令编译TestFramework这个target的arm版本
xcodebuild -project TestFrameworkDemo.xcodeproj -scheme TestFramework -configuration Release CONFIGURATION_BUILD_DIR='~/Desktop/build/arm'

// 使用xcodebuild命令编译TestFramework这个target的x86版本
xcodebuild -project TestFrameworkDemo.xcodeproj -scheme TestFramework -configuration Release -sdk iphonesimulator CONFIGURATION_BUILD_DIR='~/Desktop/build/x86'

// 合并两个版本,输出文件TestFramework
lipo -create ~/Desktop/build/arm/TestFramework.framework/TestFramework ~/Desktop/build/x86/TestFramework.framework/TestFramework -output ~/Desktop/build/TestFramework

// 将合并的文件覆盖arm/TestFramework.framework中对应文件
cp ~/Desktop/build/TestFramework ~/Desktop/build/arm/TestFramework.framework 

// 合并后的framework包移动到桌面
mv ~/Desktop/build/arm/TestFramework.framework ~/Desktop/

// 删除build文件夹
rm -r ~/Desktop/build

// 验证framework包含的框架集,如果结果是“armv7 i386 x86_64 arm64”说明没有问题
lipo -info ~/Desktop/TestFramework.framework/TestFramework

4. tip

针对项目中集成的第三方库,如果不支持某一框架导致某一框架下编译失败的问题,出现如下图所示错误:


symbolError.png

此时怎么做呢?三种办法

4.1 真实可靠地重新打包

源码在手,自行打包

4.2 打空包,当然仅能支持编译,并不支持实际功能

针对利用别人的第三方库,很大可能下拿不到源码,此时怎么做呢?只能对功能妥协,仅支持编译。

利用公开的.h文件按照前述3个步骤自行打包,然后利用lipo 命令与原有包进行合并。

此时还有一个问题,如果实际有调用入口,那么上述方法运行时依然会报找不到对应架构下的符号表错误,这是需要手动实现对应类的对应方法,当然实际上我们并不知道对应方法的具体实现,但是别忘了我们这时是处于妥协状态,对应方法直接写成空方法即可。

4.3 如果上述两种方法都无效,使用宏屏蔽第三方库调用入口

当然如果上述两种方法都无效,那么依然是要妥协的,可以使用宏屏蔽第三方库调用入口,即例如:

// 例如第三方库原有包仅支持arm,那么就可以对模拟器运行情况进行屏蔽
#if TARGET_IPHONE_SIMULATOR
#else
    // 调用第三方库的代码入口
    [IDLFaceLivenessManager.sharedInstance reset];
#endif

参考:组件化-动态库实战

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

推荐阅读更多精彩内容