简介
.a包是一种非开源代码共享程序代码的一种方式,对于接入方而言,看不到具体实现。通常随.a包提供给接入方的还有图片资源包.bundle和提供api接口的.h文件。
制作步骤
注:制作静态库通常伴随着调试demo一起,所以可以先建一个测试demo的工程,再在demo的target下以add a target方式添加静态库和图片资源包,但这种方式测试demo跟静态库文件都在一个工程目录下,个人觉得这不利于开发中协同开发和代码剥离,所以我喜欢用workspace的方式将demo工程和静态库工程进行关联,但这个可以根据个人爱好来选择自己喜欢的方式,并没有什么不妥,下面主要介绍一下自己使用的方式。
1.新建一个静态库工程,并命名为TestLib。
2.新建一个测试demo,并命名为TestDemo。
3.把TestDemo文件夹拖到TestLib文件夹下,然后打开两个工程,将TestDemo工程拖到TestLib工程里,xcode会弹出提示框让你新建一个关联项目工程的workspace,我们命名为TestLib,路径选择放在TestLib文件夹下。
关掉两个工程,打开TestLib文件夹下的TestLib.xcworkspace。可以看到,这样测试demo工程和静态库工程就同在一个workspace中了。
4.如果需要图片资源包,我们可以一个bundle。可以看到iOS下并没有bundle,不过OS X下的Framework & Library下有,其实是一样的,我们到时候改一下Base SDK就行了,我们下面新建一个bundle,命名为TLResource(通常bundle的名字与.a库的名字相同,此处仅为演示使用)。
这样target下就多了一个TLResource,下面我们改一下Base SDK,改成Latest iOS
需要注意的是,Bundle放入的@2x和@3x图片资源(.png)在默认配置下会被转为.tiff格式,使用的时候找不到。解决办法:找到bundle的工程,修改:
Buld Settings > COMBINE_HIDPI_IMAGES 设置为NO
之后在运行,可以看到图片正常了。
5.编译一下TestLib和TLRerource得到.a包.boudle图片库和.h头文件,就是我们要提供给接入方的全部文件。
6.把上面编译后得到的文件,引入到TestDemo中,注意不要勾选Copy items if needed选项,这样如果编译TestLib工程后TestDemo中的.a包就是最新的了,但是如果静态库暴露的头文件有变化的话,需要删除引入路径重新导入静态库文件。
在TestLib中开发静态库功能,在TLResource中增加图片文件,然后在TestDemo中就可以测试我们开发出来的.a 的功能。
静态库版本
跟普通app开发类似,静态库分为Debug跟release版,同时又分为真机版和模拟器版,即存在四种版本:
- Debug-iphoneos
- Debug-iphonesimulator
- Release-iphoneos
- Release-iphonesimulator
修改debug或release,只需在Edit scheme...中修改Build Configuration即可。
真机与模拟器版本,分别选择真机或模拟器运行,就会生成对应的静态库
暴露的头文件
如需添加多个暴露的头文件的话,可以用如下操作
指令集
通常做静态库提供给接入方使用的时候,需要支持多种设备,如果想自己的app在各个机器都能够最高效率的运行,则需要将Build Active Architecture Only改为NO,Valid architectures选择对应的指令集:armv7 armv7s arm64。这个会为各个指令集编译对应的代码,因此最后的 ipa体积基本翻了3倍,Release版本必须NO。
打包
上面说到,静态库分为真机、模拟器、debug、release版本,提供给接入方使用的时候,没特殊说明的话一般都是要提供一个支持在真机与模拟器上运行的release版本。这样就需要将真机与模拟器版本合并。首先我们可以在finder下看到这样的四个文件夹
使用如下命令,将release的真机与模拟器两个版本合并
lipo -create Release-iphoneos/libTestLib.a Release-iphonesimulator/libTestLib.a -output Desktop/libTestLib.a
create后面跟的两个分别是phones静态库版本和iphonesimulator版本的路径,output后面参数是合并后的版本存放路径。
合并之后,打开终端,找到合并版本,使用下面的命令
lipo -info Desktop/libTestLib.a
可以查看打包好的静态库所支持的cpu架构
Architectures in the fat file: Desktop/libJDRedPackets.a are: armv7 i386 x86_64 arm64
Architectures对应的设备
i386 :5及以下模拟器
x86_64:5s及以上模拟器
armv7:3gs~4s
armv7s:5~5c (静态库只要支持了armv7,就可以跑在armv7s的架构上)
arm64:5s及以上
开发时注意事项
由于静态库是集成在接入方工程中使用,所以要注意避免与接入方工程代码及接入方工程中引入的其他静态库发生冲突,所以在开发静态库时需要特别注意几点:
1.类名、宏定义、枚举、通知、类别等命名时加静态库统一特殊前缀,以避免命名冲突。
2.类别中方法名也需要加特殊前缀,以避免方法覆盖导致不必要麻烦。
3.对于项目中的c、c++中的方法,需要加前缀
4.对于开发静态库时引入的开源库,若体量过大,可外部引用,提供给接入方时加以说明,体量小的可以对类名及类中所用枚举、通知等加前缀使用。
5.特别需要注意的是在同一个类中多个interface的情况,加前缀时一定要检查所有的interface,避免遗漏
6.由于一些接入方引入静态库时,对app的体积有严格的控制,所以在开发静态库时,要尽量精简代码,引入开源库时,可剔除一些不必要的部分,如能用系统提供的方法实现的功能,尽量不去引入大型第三方库,不然打包出来的.a库可能体积增加很多。
引入时xcode配置
接入方引入.a包的时候,如果.a包中同时包含类和类别时,需要修改链接器参数为-ObjC
,这样链接器就会把静态库中所有的Objective-C类和类别都加载到最后的可执行文件中,否则会报unrecognized selector
错误。
当静态库中只有类别而没有类的时候,-ObjC
参数就会失效了。这时候,就需要使用-all_load
或者-force_load
了。-all_load
会让链接器把所有找到的目标文件都加载到可执行文件中,但是千万不要随便使用这个参数!假如你使用了不止一个静态库文件,然后又使用了这个参数,那么你很有可能会遇到ld: duplicate symbol
错误,因为不同的库文件里面可能会有相同的目标文件,所以建议在遇到-ObjC
失效的情况下使用-force_load
参数。
-force_load
所做的事情跟-all_load
其实是一样的,但是-force_load
需要指定要进行全部加载的库文件的路径,这样的话,你就只是完全加载了一个库文件,不影响其余库文件的按需加载。
crash日志收集
静态库的crash日志收集,应该是比较头痛的事儿,静态库是要集成到别人的项目中,当要收集crash日志时,如果引入第三方库,如果接入方也使用了相同的第三方日志收集库,难免会引起静态库间的冲突,所以这种方式似乎并不可靠。可以通过系统NSException中提供的NSSetUncaughtExceptionHandler
和NSGetUncaughtExceptionHandler
方法,收集一些简单的crash日志信息,然后将收集到的crash日志同步到服务端,以供查找问题所在,但这种方式还是很局限,目前对于crash日志收集还需学习更多知识,此部分待续。