Leon’s Blog

Dreams don't work unless you do.

iOS打包静态库(一)

前段时间由于工作的调动,接触到了游戏SDK的开发工作,最终要打包成静态库供游戏商使用,在这里做下记录。

为什么要打包成静态库?

举个栗子,比如支付宝,百度地图SDK中,他们仅仅是给我们暴露出来了.h文件中的接口,具体的实现隐藏到了静态库中,因为他们不想让别人拿到自己公司私密性的内容,这就是静态库的作用(使用场景)。

什么是库?

库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。
iOS中的静态库有 .a 和 .framework两种形式;动态库有.dylib 和 .framework 形式,后来.dylib动态库又被苹果替换成.tbd的形式。

静态库和动态库的介绍

  • 静态库:静态库即静态链接库(Windows下的.lib,Linux 和 Mac 下的.a。之所以叫做静态,是因为静态库在编译的时候会被直接拷贝一份,复制到目标程序里,这段代码在目标程序里就不会再改变了。
    静态库的好处很明显,编译完成之后,库文件实际上就没有作用了。目标程序没有外部依赖,直接就可以运行。当然其缺点也很明显,就是会使用目标程序的体积增大。
  • 动态库:动态库即动态链接库(Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib)。与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。
    动态库的优点是,不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库)。同时,编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行(Linux 下喜闻乐见的 lib not found 错误)。

iOS里静态库和动态库的存在形式

  • 静态库:.a 和 .framework
  • 动态库:.dylib(.tbd) 和 .framework

静态库与动态库的区别?

  • 静态库:链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝。
  • 动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。
  • .a文件是一个纯二进制文件,至少需要配合.h文件去使用,如果需要资源文件,要在把资源文件放在外部并不能把资源文件一同打包成.a文件。
  • .framework相当于.a + .h + sourceFile的整合,打包过程中可加入其它.a文件进行一同打包成最终的.framework文件。
(注意:.framwork中将图片资源或.xib 加入到其中也是读取不到的,因为iOS系统不会去扫描.framework下的资源文件。当然,我们可以放入一些说明文件在其中作为说明文件,查看起来还是清晰明了哒。)

实践

我们在打包.a文件时可能会遇到这3种情况:

  1. 把.h和.m文件打包成.a文件和.h文件。
  2. 把.a文件和.h文件打包成新的.a文件和.h文件。
  3. 被打包的文件含有.bundle资源文件。

第一种情况直接将.h和.m添加到打包的工程中进行打包即可。
第二种情况相当于把.a文件打包成新的.a文件,把.a文件和.h文件一并拖入到打包的工程中进行打包即可。
第三种情况.bundle资源文件不参与打包过程,把.a文件和.h文件拖入到打包的工程生成新的.a和.h文件,再匹配.bundle文件使用即可。

举个栗子,我们要把一个包含.bundle文件的第三方库(以SVProgressHUD为例)打包成.a静态库(下面的过程就是打包静态库.a的过程,第1,2种情况按正常流程打包,主要针对第3中情况进行说明.bundle文件为什么不能进行打包):

  • 第一步,先创建一个打包.a静态库的工程:
  • 第二步,把我们下载好的SVProgressHUD文件拖入到工程内:
  • 第三步,如图操作:
  • 第四步,如图操作:
  • 第五步,commad + B 真机编译一下然后再模拟器编译一下,为什么要真机编译呢?因为如果我们不真机编译的话,如第三步图中左下角标注的.a包将始终是红色的,我们将获取不到文件的生成路径,真机编译和模拟器编译会出来两个分别支持真机和模拟器的.a包,如果我们需要.a静态库在真机和模拟器都要支持的话,需要执行
    lipo -create /Users/harvey/Library/Developer/Xcode/DerivedData/FMDB-clvayfrjgytqrbdkyqrtcjkxfeuz/Build/Products/Release-iphonesimulator/FMDB.framework/FMDB /Users/harvey/Library/Developer/Xcode/DerivedData/FMDB-clvayfrjgytqrbdkyqrtcjkxfeuz/Build/Products/Release-iphoneos/Release-iphoneos.framework/FMDB -output /Users/harvey/Desktop/FMDB
    
    将它们合并下即可。因为我只是想在模拟器测试一下,所以就偷懒不合并了^ ^, 编译后查找到.a文件的路径如下:

    usr文件下是我们暴露出来的.h文件
  • 第六步,我们新建一个测试的工程,将SVProgressHUD.h(我暴露出来的头文件)和libLibraryPackag.a 导入到测试工程:
  • 第七步,在ViewController实现文件导入SVProgressHUD.h头文件,调用它的loading方法
  • 第八步,command + R 运行,发现crash了,看下控制面板:
  • 第九步,知道原因后,我们重新去打包静态库的工程打包一遍,这次我们把第二步中的红框.bundle文件移除掉打包,然后把新打包成功的.a文件 + 刚才移除掉的.bundle文件直接拖入到测试工程内,如图:
  • 第十步,再command + R运行如下:

如果这时候我们需要把libLibraryPackage.a与另一个.a文件进行合并,另一个.a文件比如叫otherlibLibraryPackage.a,这时候我们只需要把libLibraryPackage.a和otherlibLibraryPackage.a添加到打包静态库的工程中打包成一个全新的.a静态库,具体操作重复上述步骤进行就可以了。