iOS开发之关于静态库制作及注意点


在那时候,那些东西不转身就正面背面都领受到了月光,我不回头就看见了过往。

–刘亮程《今生今世的证据》

前言

  • 使用库无外乎就是两种情况:一种是将源码的实现细节隐藏,只给外部调用的接口集成特定的服务,如常见的百度/谷歌地图等等第三方库;另一种就是防止公司的核心代码泄露,将核心功能的源码打成库文件集成。
  • 静态库的制作并不难,但是像我这样距离上次制作静态库都几年了的人应该也不少,重新上手又要去查指令什么的😂,所以就将过程及注意点记录下来,以后再需要就比较好翻阅了。

关于库

  • 库一般分为静态库和动态库两种,其本质上来说是可以被载入内存中执行的可执行代码的二进制格式。
  • iOS中的静态库有.a.framework两种形式;动态库有.dylib.framework 形式,.dylib动态库从Xcode 8开始以.tbd的形式出现。

  • 动态库和静态库的区别:

    • 静态库在链接时会被完整的复制到可执行文件中,被多次使用就有多份冗余拷贝。
    • 动态库链接时不复制,程序运行时由系统动态的加载到内存供程序调用,系统只加载一次,多个程序共用,节省内存。
  • .a静态库 和 .framework静态库区别

    • .a静态库是纯二进制文件,不能单独使用,一般需要配合头文件使用。
    • .framework静态库可以单独使用,并且可以集成资源文件,类似于.a + .h + sourceFile = .framework

.a静态库

库文件生成

  • 新建项目,如图选择新建静态库:

    • 静态库命名一般以lib开头,后面加上静态库的功能,如我这个静态库用于蓝牙加解密,所以命名为libBLECrypto
      staticLibrary_creat
  • 完成之后会自动生成项目名称同名的.h.m文件。

  • 我们根据需求实现功能及导入相关的文件,此时如果我们不编译,默认Products目录下会有一个红色的lib+ 项目名称 + .a的文件。

    staticLibrary_edit
  • Command + B 编译一下项目,此时该文件变为黑色。我们在该.a文件上右键Show in Finder,进入其所在位置,实质上项目中的.m文件已经生成.a文件了。但是此时别人是无法使用的,因为缺少对应的头文件,我们需要再如下位置配置需要暴露给外界的头文件信息:

staticLibrary_importHeader
  • 配置完成后重新Command + B 编译后会发现在原来生成的.a文件的同级目录会生成一个include目录,里面就是需要一起提供给调用者的头文件信息。
  • 进行到这里是不是感觉.a静态库的制作很简单?不要闹,我们还有更重要的事情没有解决呢。

环境及平台处理

  • 我们知道生成的静态库可能用于Debug模式,也可能用于Release模式;可能是在模拟器上面运行,也可能是在真机上运行,既然有这么多可变因素,那么我们就需要一一解决。
  • 我们首先将不同的开发模式和不同的运行环境下生成对应的库文件(共4种):
    • 模拟器 + Debug
    • 模拟器 + release:
    • 真机 + Debug
    • 真机 + release
staticLibrary_compare
  • 这里有一个注意点,需要将构建建构设置为NO,否则只支持选中的设备架构:
staticLibrary_buildActive
  • 使用lipo -info命令查看静态库支持的CPU架构,如
staticLibrary_architecturesCompare
  • 可以看出使用真机编译生成的静态库是支持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 SettingsOther C Flags 添加-fembed-bitcode参数。

      staticLibrary_architecturesCompare
  • 检查生成的静态库文件是否支持Bitcode,请使用如下指令:

1
2
3
otool -arch armv7 -l libBLECrypto.a 
或者
otool -arch arm64 -l libBLECrypto.a
  • Command + F搜索bitcode字段,如果存在sectname为bitcode的字段且对应的size不是0x0000000000000001,说明静态库支持Bitcode
staticLibrary_architecturesCompare

framework静态库

  • 用到了再补充🌝🌚。

注意点

  • 如果静态库中使用到分类,那么直接使用静态库会有找不到该方法的运行时错误(selector not recognized),解决办法是:在使用静态库的工程中配置other linker flags的添加-ObjC
  • 如果一个静态库很复杂,需要暴露的.h比较多的话,就可以在静态库的内部创建一个.h文件(一般这个.h文件的名字和静态库的名字相同),然后把所有需要暴露出来的.h文件都集中放在这个.h文件中,而那些原本需要暴露的.h都不需要再暴露了,只需要把.h暴露出来就可以了。
  • 如果我们在同一电脑行生成静态库并配置到其他项目中,此时在项目中打断点是可以进入静态库的实现文件中的,不过不用担心,在其他电脑是以汇编形式显示的。
要不要鼓励一下😘