在那时候,那些东西不转身就正面背面都领受到了月光,我不回头就看见了过往。
–刘亮程《今生今世的证据》
前言
- 使用库无外乎就是两种情况:一种是将源码的实现细节隐藏,只给外部调用的接口集成特定的服务,如常见的百度/谷歌地图等等第三方库;另一种就是防止公司的核心代码泄露,将核心功能的源码打成库文件集成。
- 静态库的制作并不难,但是像我这样距离上次制作静态库都几年了的人应该也不少,重新上手又要去查指令什么的😂,所以就将过程及注意点记录下来,以后再需要就比较好翻阅了。
关于库
- 库一般分为静态库和动态库两种,其本质上来说是可以被载入内存中执行的可执行代码的二进制格式。
iOS中的静态库有
.a
和.framework
两种形式;动态库有.dylib
和.framework
形式,.dylib
动态库从Xcode 8开始以.tbd
的形式出现。动态库和静态库的区别:
- 静态库在链接时会被完整的复制到可执行文件中,被多次使用就有多份冗余拷贝。
- 动态库链接时不复制,程序运行时由系统动态的加载到内存供程序调用,系统只加载一次,多个程序共用,节省内存。
.a
静态库 和.framework
静态库区别.a
静态库是纯二进制文件,不能单独使用,一般需要配合头文件使用。.framework
静态库可以单独使用,并且可以集成资源文件,类似于.a + .h + sourceFile = .framework
。
.a静态库
库文件生成
新建项目,如图选择新建静态库:
- 静态库命名一般以lib开头,后面加上静态库的功能,如我这个静态库用于蓝牙加解密,所以命名为
libBLECrypto
。
- 静态库命名一般以lib开头,后面加上静态库的功能,如我这个静态库用于蓝牙加解密,所以命名为
完成之后会自动生成项目名称同名的
.h
和.m
文件。我们根据需求实现功能及导入相关的文件,此时如果我们不编译,默认
Products目录
下会有一个红色的lib+ 项目名称 + .a
的文件。Command + B
编译一下项目,此时该文件变为黑色。我们在该.a文件
上右键Show in Finder
,进入其所在位置,实质上项目中的.m文件已经生成.a文件了。但是此时别人是无法使用的,因为缺少对应的头文件,我们需要再如下位置配置需要暴露给外界的头文件信息:
- 配置完成后重新
Command + B
编译后会发现在原来生成的.a文件的同级目录会生成一个include目录,里面就是需要一起提供给调用者的头文件信息。 - 进行到这里是不是感觉.a静态库的制作很简单?不要闹,我们还有更重要的事情没有解决呢。
环境及平台处理
- 我们知道生成的静态库可能用于Debug模式,也可能用于Release模式;可能是在模拟器上面运行,也可能是在真机上运行,既然有这么多可变因素,那么我们就需要一一解决。
- 我们首先将不同的开发模式和不同的运行环境下生成对应的库文件(共4种):
- 模拟器 + Debug
- 模拟器 + release:
- 真机 + Debug
- 真机 + release
- 这里有一个注意点,需要将构建建构设置为
NO
,否则只支持选中的设备架构:
- 使用
lipo -info
命令查看静态库支持的CPU架构,如
- 可以看出使用真机编译生成的静态库是支持armv7 arm64架构的,使用模拟器编译生成的静态库是支持i386 x86_64架构的(armv7是兼容armv7s的)。
关于设备的CPU架构(指令集)
模拟器:
- i386 : 32位架构 4S ~ 5
- x86_64 : 64位架构 5S ~ 现在的机型
真机(iOS设备):
- armv7 : 32位架构 3GS ~ 4S
- armv7s: 特殊的架构 5 ~ 5C (此架构有问题, 有的程序变得更快, 有的程序变得更慢)
- amr64 : 64位架构 5S ~ 现在的机型
根据使用者的需求,将对应的库文件及头文件提供给调用者即可,但是如果在模拟器和真机之间切换使用这样比较麻烦,所以需要将两个静态库合并为一个静态库供别人使用。
静态库合并
- 将静态库合并也比较简单,只需要需要支持的架构的静态库合并即可,一般我们主要合并模拟器Release下的静态库和真机Release下的静态库,保证在模拟器和真机下都可以调试并且可以发布应用。合成指令如下:
1 | lipo -create 静态库1.a 静态库2.a -output 新静态库.a |
- 合成完成会生成一个新的.a静态库,将该.a静态库和头文件一起提供给调用者即可。
关于静态库上架
- 如果项目中导入了
.a
静态库且上架需要开启Bitcode
选项,那么静态库就需要支持Bitcode
才可以,否则打包上架会遇到如下问题:
1 | ld: bitcode bundle could not be generated because 'xxx.a(xxx.o)' was built without full bitcode. |
让静态库支持,需要在编译生成静态库文件前进行下面两步配置
- 需要在
Build Settings
中设置Enable Bitcode
选项为Yes
(默认是开启状态)。 在
Build Settings
的Other C Flags
添加-fembed-bitcode
参数。
- 需要在
检查生成的静态库文件是否支持
Bitcode
,请使用如下指令:
1 | otool -arch armv7 -l libBLECrypto.a |
Command + F
搜索bitcode字段,如果存在sectname
为bitcode的字段且对应的size不是0x0000000000000001
,说明静态库支持Bitcode
。
framework静态库
- 用到了再补充🌝🌚。
注意点
- 如果静态库中使用到分类,那么直接使用静态库会有找不到该方法的运行时错误(selector not recognized),解决办法是:在使用静态库的工程中配置
other linker flags
的添加-ObjC
。 - 如果一个静态库很复杂,需要暴露的.h比较多的话,就可以在静态库的内部创建一个.h文件(一般这个.h文件的名字和静态库的名字相同),然后把所有需要暴露出来的.h文件都集中放在这个.h文件中,而那些原本需要暴露的.h都不需要再暴露了,只需要把.h暴露出来就可以了。
- 如果我们在同一电脑行生成静态库并配置到其他项目中,此时在项目中打断点是可以进入静态库的实现文件中的,不过不用担心,在其他电脑是以汇编形式显示的。