前言
由于公司业务,笔者近段时间内频繁与iOS的SDK打交道,其中包括制作,打包,以及给第三方(CP)使用时的坑以及问题。在这里把笔者所遇到的坑以及得到的经验分享一下。
静态库与动态库
库即Library,即.a .framework .tbd .dylib
后缀的文件。
.a
静态库,常见于第三方的SDK,如微信的SDK。.framework
既有静态库也有动态库,由于WWDC2014
既Xcode 6
之前官方是不给使用动态库的,所以市面上的第三方SDK一般都是静态库。.tbd
常见于Xcode 7
后,系统的动态库。.dylib
动态链接库,可由开发者制作,一般常见于逆向工程,可否上架,笔者没试过,据说是不行。与.framework
不同的地方是,.framework
一般在工程项目里就要添加一起编译,而.dylib
可以在App已生成的情况下再动态注入。
区别
可以根据区别,选择自己所要制作的SDK类型。
静态库
例子:支付宝SDK,微信SDK
- 需手动导入静态库所依赖的其他类库,如上述例子的SDK,需导入大量的系统依赖库。
- 部分第三方的SDK的会依赖一些流行的第三方框架,例如
SVProgressHUD``AFNetworking
等,这样可避免静态库重复添加相同的文件导致编译不成功。 - 静态库在编译的过程中,只会参与链接的操作,链接器会将静态库中被使用的部分合并到可执行文件中去,用函数 的实际地址来代替函数引用,通俗一点的说法就是一起编译到App可执行二进制文件里了。
动态库
我们最常用的一类库,使用频率最高的UIKit.framework和Fundation.framework都属于动态库。但我们所制作的动态库与系统的动态库有所不同,不同的地方在于它的权限。
- 系统动态库一般在需要的时候,会从系统动态加载。我们开发者自己制作的动态库,只能在
iOS8.0
以上的环境,而且仅运行于沙盒中,其他App无法使用。不过iOS8
上开放了App Extension
功能,可以为一个应用创建插件,这样主app和插件之间共享动态库还是可行的,不过笔者并未尝试过,有兴趣的可以自己试下。 - 开发者自己打包的动态库可以导入第三方的库以及系统依赖库,且不需要在使用的工程环境下添加依赖。
- 动态库,与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。
SDK制作,Framework
首先要明白自己所要制作的SDK,要用动态库实现,还是要用静态库。笔者在这给几个例子。
- 渠道SDK,CP(内容提供商,一般指游戏方)分包用的SDK,动态库,依赖许多第三方SDK,系统库等的情况下推荐使用动态库,若是只存在内购的情况下,还是推荐你使用静态库的。
- 数据追踪SDK,工具类SDK,依赖少量或者不依赖第三方库,只是开放接口让别人使用代码里的功能,静态库。
- 使用SDK的项目最低支持系统版本低于
iOS8.0
,动态库iOS8.0后才支持,若项目支持最低系统版本低于iOS8.0
的,建议使用静态库,避免系统版本低于iOS8.0
的设备发生不可预知的错误。 #####以笔者的Xcode 8
为例,演示一遍静态库Framework的制作以及打包。.a
文件的静态库跟.framework
大同小异,选择下图的Cocoa Touch Static Library
即可,篇幅有限,笔者遍不在此演示了。 - 新建工程
- 配置部署环境 根据自己的需求配置SDK所支持的
iOS
系统版本 - 选择编译类型,若是动态库则选择
Dynamic Library
,静态库则如图下所示 - 添加依赖的第三方库以便编译成功 此处,笔者以
SVProgressHUD
示例。- 若是依赖系统库,静态库不用在SDK里处理,动态库则需要通过
Project->General->Link Frameworks and Libraries
添加对应依赖的系统库。 - 若是依赖系统lib开头的dylib动态库,报图下原因的错误的话,大多数原因是在于你静态库动态链接了系统动态库的问题,将上图
Mach-O Type
改为Dynamic Library
即可。
- 若是依赖系统库,静态库不用在SDK里处理,动态库则需要通过
- 选择公开的
.h
文件 根据下图操作,或者直接将所要公开的.h
文件拖到Public
下即可,也可以在.h
文件右边的Target Membership
改为Public
。 - Command + B 编译 编译成功后,右键Products目录下生成的SDK到相应的Finder目录。可以看到如图下的效果。
- 真机SDK只能真机环境下使用。
- 模拟器SDK只能模拟器环境下使用。
- 真机模拟器的静态库合并 合并只是为了,真机测试和模拟器测试都方便一些。上传发布审核的情况下只能用真机的SDK,不然会导致审核失败。
- 在终端下使用命令行
lipo -create [真机SDK路径] [模拟器SDK路径] -output [合并路径包含文件名]
- 将生成的SDK拖入真机
framework
目录覆盖,便是真机和模拟器都可以使用的SDK了。
- 在终端下使用命令行
静态库,动态库使用
静态库
在上一步的制作中,我们制作了一个TestSDK
的静态库,接下来我演示下动态库与静态库配置到工程目录下时候的区别。
- 下图我故意没有添加
SVProgressHUD
到工程目录 - 编译的话会出现如图下所示错误
- 添加所依赖的
SVProgressHUD
到工程目录 - 还有一种情况,若是静态库中存在
Catagory
分类,可能会报错,提示找不到方法unrecognized selector sent to class
。 - 在
Project->Build Settings->Linking->Other Linker Flags
增加-ObjC
,即强制把所有第三方库中定义的Objective-C类和Category加载进来。 - 若是
-ObjC
导致与工程目录下其他SDK发生冲突报错,则使用-force_load [Framework目录里的SDK二进制文件的路径]
,即强制把SDK的内容加载进来。 ####动态库 笔者将上文所制作的TestSDK
静态库,改为了动态库,接下来我会演示下动态库配置到工程目录下的一些可能会遇到的问题。 - 动态库导入方式不对会出现如图下的错误
- 应通过
Project->General-> Embedded Binaries导入
的方式导入,如图所示 - 重复导入相同的第三方库或文件导致的错误,可能
SVProgressHUD
内部做了处理,笔者这里尝试只有警告,其他库或者同名文件则会导致报错,所以各位同学若是制作自己的SDK时,务必加上属于自己文件的前缀,避免重名,重复添加的错误。
Bundle多媒体资源(图片,音频,视频)打包
很多情况下,我们的SDK都可能用到图片,音频,视频等,但这些文件不能通过静态库来封装,封装Bundle
文件便可解决我们的需求,就像SVProgressHUD
目录下的Bundle文件一样。
- 创建Bundle文件
- 笔者在这演示的并不是唯一的Bundle文件创建方法,可能这不是最好的方法,但也是能解决我们的需求的方法之一。
- 创建成功后,到
Build Settings->Architectures->Base SDK
修改为iOS
所用的Bundle文件,再修改Deployment info->Deployment Target
的版本号。 - 拉入或导入多媒体资源。
- png图片如果用了@2x 、@3x会自动转换成tiff格式的图片。设置不转换的话通过Bundle的
target->Build Settings->User-Defined->COMBINE_HIDPI_IMAGES
设置为NO即可。 Bundle
二进制文件问题,根据笔者这样创建Bundle
的话,Bundle
目录下的info.plist
中的Executable file
一项要删除。不然会导致审核上传app时出现ERROR ITMS-90166
或者ERROR ITMS-90171
的错误。
如果在开发过程中遇到什么问题,请在评论区留言,笔者看到会第一时间回复你,或是文章有什么写错的,请大胆指出,笔者也会在第一时间修正。