一、前言
“隔着一段距离看,很多有趣的知识看起来都很唬人。”在我初出茅庐的时候着实觉得那些后缀名为“.frameworke”、“.a”、“.dylib”的文件很神秘,很高冷。那时我虽然知道只要导入一个库就能引用库里面很多封装好的东西,但对这个“库”究竟是什么“鬼”,一直都是云里雾里。后来开发中有打包的需求,然后就去找了些文章,但是都不是太全面,而且自己操作中有些小细节的地方也是挺坑的,就想写下自己打包过程中遇到的细节和坑,希望对你们有帮助。好了废话不多说,看下去就知道它是个什么“鬼”。
二 、一些概念的补充
1、 什么是库?
所谓库就是程序代码的集合,是共享程序代码的一种方式。
2、 库的分类
根据程序代码的开源情况,库可以分为两类
开源库
源代码是公开的,你可以看到具体实现。比如GitHub上比较出名的第三方框架AFNetworking、SDWebImage。
闭源库
不公开源代码,只公开调用的接口,看不到具体的实现,是一个编译后的二进制文件。这种常见于一些公司的SDK包,比如高德地图SDK、环信即时通讯SDK等等。而闭源库又分为两类:静态库和动态库。本篇重点要讲的便是其中的静态库。
3、静态库和动态库的存在形式和使用区别
存在形式:
静态库
以".a"或者“.framework”为文件后缀名
动态库
以".dylib"或者“.framework”为文件后缀名
使用区别:
静态库链接时会被完整的复制到可执行文件中,被多次使用就有多份拷贝。
静态库被程序使用时
动态库链接时不复制,程序运行时由系统动态加载到内存,供程序调用。而且系统只加载一次,多个程序共用,节省内存。
4、iOS 设备的CPU架构
模拟器:
4s-5: i386
5s-7 Plus: x86_64
真机(iOS设备):
armv6: iPhone、iPhone 2、iPhone 3G、iPod Touch(第一代)、iPod Touch(第二代)
armv7: iPhone 3Gs、iPhone 4、iPhone 4s、iPad、iPad 2
armv7s: iPhone 5、iPhone 5c (静态库只要支持了armv7,就可以在armv7s的架构上运行)
arm64: iPhone 5s、iPhone 6、iPhone 6 Plus、iPhone 6s、iPhone 6s Plus、iPad Air、iPad Air2、iPad mini2、iPad mini3
注:真机iPhone7、iPhone7 Plus A10处理器到底是什么架构暂时不得而知,没查到相关资料,貌似还没公布,但是模拟器是x86_64。
5、a与.framework有什么区别?
.a是一个纯二进制文件,.framework中除了有二进制文件之外还有资源文件。
.a文件不能直接使用,至少要有.h文件配合,.framework文件可以直接使用。
.a + .h + sourceFile = .framework。
建议用.framework.
6、制作静态库时的几点注意:
1 注意理解:无论是.a静态库还.framework静态库,我们需要的都是二进制文件+.h+其它资源文件的形式,不同的是,.a本身就是二进制文件,需要我们自己配上.h和其它文件才能使用,而.framework本身已经包含了.h和其它文件,可以直接使用。
2 图片资源的处理:两种静态库,一般都是把图片文件单独的放在一个.bundle文件中,一般.bundle的名字和.a或.framework的名字相同。.bundle文件很好弄,新建一个文件夹,把它改名为.bundle就可以了,右键,显示包内容可以向其中添加图片资源。
3 category是我们实际开发项目中经常用到的,把category打成静态库是没有问题的,但是在用这个静态库的工程中,调用category中的方法时会有找不到该方法的运行时错误(selector not recognized),解决办法是:在使用静态库的工程中配置other linker flags的值为-ObjC。
4 如果一个静态库很复杂,需要暴露的.h比较多的话,就可以在静态库的内部创建一个.h文件(一般这个.h文件的名字和静态库的名字相同),然后把所有需要暴露出来的.h文件都集中放在这个.h文件中,而那些原本需要暴露的.h都不需要再暴露了,只需要把.h暴露出来就可以了。
三、进入主题:打包静态库!
.a文件静态库打包
1、打开Xcode创建一个新的工程,这里以Xcode8为例,选择工程如下:
2、创建工程完毕后,界面如下(以打包SDWebImage为例)
系统会默认帮我们生成两个文件,可以只留一个.h文件然后把SDWebImage框架的所有文件拖拽到工程里面,然后把想暴露的头文件写在SDWebImage.h里面(也可以俩个文件都删除,把框架的文件都拖拽进去)。
我们知道静态库存在的最大意义是隐藏代码的具体实现,但是这也隐藏的太彻底了,总要公开些接口或者头文件供人调用吧。
3、公开接口头文件
targets->Build Phases->Copy Files->"+"你需要公开的头文件
选择需要对外公开的.h(建议添加所有的.h,如果你只想对外提供部分的.h,要保证这些.h里面没有导入其他未添加到Copy Files的.h,否则你在编译后生成的.a在使用的时候就会报错)输入.h,然后command+a 全选,点击Add
4、设置适配所有架构
project -> buildSeting -> Build Active Architecture Only 设为NO
5、运行工程进行打包
command + b 编译就会生成静态库了,
Debug-iphoneos 这个就是Debug模式的真机静态库,
可以通过选择Run的info设置 Debug 和 Release,设置Debug和Release。
通过选择Generic iOS Device 和 模拟器,设置真机和模拟器。
所以你会得到四种静态库:
Debug-iphoneos Debug-iphonesimulator
Release-iphoneos Release-iphonesimulator
6、终端查看静态库所支持的架构
终端->cd进入库文件路径(Debug-iphoneos这个目录下面)->lipo -info 库名(lipo -info libSDWebImage.a) 文件的路径:可以通过直接拖拽文件到终端
或者 终端->lipo -info .a的路径
可以看到支持 armv7 arm64(静态库只要支持了armv7,就可以在armv7s的架构上运行)
如果不设置第4步:project -> buildSeting -> Build Active Architecture Only 设为NO,生成的静态库只会支持当前选择的模拟器或者真机的架构。
7.合并真机和模拟器的静态库
进入电脑的终端命令,进入到product的目录文件下,执行命令cd+product的文件路径
lipo -create 静态库1路径 静态库2路径 -output 合并的静态库名字(libSDWebImage.a)
lipo -info 静态库路径 查看静态库支持的架构,可以看到能够支持所有的架构了。
看了好多文章有说Debug和Release也能合并,但是没搞出来,有成功的麻烦也告知下我。
.frameworke文件静态库打包
1、依然Xcode创建一个新的工程FrameworkeLib,选择工程如下:(以打包MJRefresh为例)
2、创建完成后我们可以看到,工程本身自带一个MJRefreshFramework.h文件,这是类似一个主头文件一样的东西。
然后把SDWebImage框架的所有文件拖拽到工程里面。
这里将MJRefresh.h删除了(不需要了),需要暴露的头文件写在MJRefreshFramework.h里面,以这样的形式#import
3、设置支持所有模拟器架构或真机架构(和打包.a第4步骤一样)
4、公开头文件
target-Build Phases - Headers -把需要公开的头文件从project拖入Public 或者右键Move to public Group(这里将全部头文件添加进去,根据需求添加想要暴露的.h,不要出现Public里的.h有import Project里的.h,被坑过)
5、设置打包的是静态库。因为动态库也可以是以framework形式存在,所以需要设置,否则默认打出来的是动态库
target->BuildSetting ->搜索关键字mach->Mach-o Type 设为Static Library(这个默认选项是动态的)
6、选中真机(可以选择手机或者Generic iOS Device)或模拟器运行设备打包(与打包.a一样command + b),完成后Products文件夹下的MJRefreshFramework.framework文件由红色变成了黑色,右键show in finder
合并和查看静态库支持的架构和上面一样
四、使用静态库
使用的时候把合并的静态库拖拽到工程即可(如果有资源文件如bundle,还需要手动添加进去,系统不默认添加)
还有最近刚出xcode9试了下,好像编译打包在Products下面是没有静态库的,只好用xcode8了,还有如果用xcode9打开的项目,你在拖拽打包好的静态库到工程里面后Targets->select you target-->Build Phases->Link Binary With Libraries里面不会自动添加静态库,需要手动添加(很扯淡)
END