一、简介
I. 库的类型分类
静态库
- 以
.a
或.framework
为文件后缀名
.a
是一个二进制文件,不能直接拿来用,使用时需要.a
文件 + 头文件 + 资源文件
静态库打包时,只能打包代码,图片文件、本地 JSON 文件和 xib 等资源文件无法打包进去 - 静态库 连接时 完整的复制到可执行文件中,多个文件使用,多份拷贝
- OC 建议使用静态库,OC 使用动态库审核可能会不通过
- 静态库无法再包含其他的 .a 静态库。只能把源码放进去一起编译
动态库
- 以
.dylib
或.framework
为文件后缀名「Xcode7 后.dylib
变为.tbd
文件」
.framework
文件,能直接拿来用。.framework
文件 = 二进制文件(.a 文件 + .h 文件) + 资源文件
.tbd
文件,只是一个文本文件,其中包含架构信息,以及在真实运行时候二进制所在的位置,以及包含了动态库的符号表还有类的一些信息,这些信息在编译阶段足够了。「减少了所有设备 SDK 二进制动态库的体积」 - 动态库 运行时 动态加载到内存,只加载一次,多个文件公用,节省内存
- Swift 只能使用动态库,不支持静态库
- 大部分第三方库就是动态库,可以暴露出来,放在源码的外部引用使用
- 动态库的特性使得软件版本实时模块升级、应用插件化、etc
II. iOS 设备的 CPU 架构
iOS 库的打包,根据 CPU 架构的不同而不同
架构不同,不能编译通过
模拟器的 CPU 架构
- i386:iPhone 4s ~ 5
- x86_64:iPhone 5s ~ 7 Plus
真机的 CPU 架构
- 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 的架构上运行」
- armv64:iPhone 5s、iPhone 6、iPhone 6 Plus、iPhone 6s、iPhone 6s Plus、iPad Air、iPad Air2、iPad mini2、iPad mini3
二、打包静态库
I. .a 文件 静态库打包
- 创建静态库的工程
创建后执行 运行 或 编译 ,都可以生成静态库「保存在项目的Products
中」
- 通过在不同架构的设备下 编译/运行 生成支持不同架构的静态库
- 在 编译/运行 时,所有项目 都可以设置是 Debug 还是 Release
- 设置库的接口 头文件
- 通过设置不仅在当前运行的 CPU 架构上,适配所有的机型号架构
一般 debug = no,release = yes 为了 debug 的时候编译更快「只编译连接当前的 CPU 架构所用的包」
可以控制 测试版本 和 发布版本 是否都不仅仅能在真机上使用
II. .framework 文件 静态库打包
- 前三步和 在 .a 文件 打包方式一致
- 设置库的接口 头文件
- 设置打包的是 静态库,因为动态库也可以以 framework 的形式存在
设为Static Library「这个默认选项是动态的」
III. 使用静态库
1. 防止项目中的文件和 静态库的同名文件在运行时会覆盖,只保留一张图片
- 把图片文件单独的放在一个 .bundle 文件中「一般 .bundle 的名字和
.a
或.framework
的名字相同」 - .bundle 文件制作方法:将文件夹,重命名为 XXX.bundle
同理,其他资源文件也放在一个 .bundle 中
2. 在使用 category 静态库的工程中,调用方法时,会出现找不到该方法的运行时错误:selector not recognized
原因:
- Unix 的标准静态库实现和 Objective-C 的动态特性之间有一些冲突
- Objective-C 没有为每个函数定义链接符号,它只为每个类创建链接符号
- 当在一个静态库中使用类别来扩展已有类的时候,链接器不知道如何把类原有的方法和类别中的方法整合起来
解决办法:
- 在使用静态库的工程中配置
other linkerflags
的值为-ObjC
如果还崩溃,在添加-all_load
「或者-force_load
」 - all_load 作用于所有的库,而 -force_load 后面必须要指定具体的文件
这样,会将静态库中所有和对象相关的文件都加载进来
3. 避免暴露过多的 .h 文件
使用
- 在静态库的内部创建一个 .h 文件「一般这个.h文件的名字和静态库的名字相同」
然后把所有需要暴露出来的 .h 文件名都集中放在这个 .h 文件中,只需要把这个 .h 暴露出来 - 一般这个总头文件内部引入其他头文件的格式为
#import <framework名字/其他头文件.h>
,这是以调用这个库的项目的角度来写的
搜索头文件
Header Search Paths
系统的搜索路径,用来引入项目中没有添加的 header 文件
通过#import <名称.h>
来引入User Header Search Paths
用户的搜索路径,用来引入项目中的已添加的 header 文件
通过#import <名称.h>
或#import "名称.h"
来引入Always Search User Paths
Header Search Paths 作为系统级别路径一定会被搜索
设 Always Search User Paths 为 YES,编译器会先搜索 User Header Search Paths 路径下的目录
设 Always Search User Paths 为 NO,编译器不会搜索 User Header Search Paths 路径下的目录「默认」设置格式举例:
$(SRCROOT)/项目名称/AppDelegate.h
$(SRCROOT)
宏和$(PROJECT_DIR)
宏都指 xxx.xcodeproj 所在的父目录,新建项目后的目录为:
Project 的 Building Settings 中得设置默认并不被 Targets 继承
只有 Targets 的设置加入了$(inherited)
时才被继承,添加目录的时候写上$(inherited)
就表示从 frameworks 里面读取设置查找路径的参数
recursive:遍历该目录,编译的时候在找库的路径的时候,会遍历该目录下的所有子目录的库文件
non-recursive:不遍历该目录「默认,推荐使用,可减少编译速度」
4. 拖拽项目/将项目当做静态库处理
- 给当前项目在
Link Binary With Libraries
中添加拖拽项目的 库文件 - 如果当前的 Target 需要依赖其他库文件,在
Target Dependencies
中添加所需的库文件 - 如果库文件在
Link Binary With Libraries
中已经添加「必要」,但也可能需要在Target Dependencies
中再次添加「非必要」
5. 其他
查看静态库所支持的 CPU 架构
在命令行找到静态库所在的文件夹,执行lipo -info 静态库文件名
命令合并静态库
在命令行找到静态库所在的文件夹,执行lipo -create 静态库1 静态库2 -output 新静态库名称.a
命令
三、打包动态库
1. 制作、编译过程与静态库相同
2. 在 Embedded Binaries
中添加动态库
3. 合并动态库文件
合并动态库文件并非合并的是 .framework
文件,而是其中的二进制代码文件
4. 动态库的使用注意
I. 自定义的 .a
静态库,不可以包含动态库
II. 自定义的 .framework
静态库,可以包含动态库文件
绕过 Xcode 的 UI 界面来连接动态库,步骤如下
- 项目中不需要引用连接的动态库「.tbd 的动态库」
- 通过格式:
-l<library_name>
添加动态库,例:添加libiconv.tdb
记为-liconv
缺陷
- 虽然自定义的静态库中已经导入了动态库 X.tbd ,但是,当自定义的 framework 的静态库被调用时,可能需要再次导入动态库 X.tbd 到当前项目中