库是共享代码的方式,根据源代码的公开情况,库可以分为两种类型:
- 开源库
公开源代码,能看到具体实现,比如SDWebImage、AFNetworking。 - 闭源库
不公开源代码,是经过编译后的二进制文件,看不到具体实现。
主要分为:静态库、动态库。
静态库:
1、平时我们用的第三方SDK基本上都是静态库。
2、静态库在项目编译时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。
3、静态库很大的一个优点是减少耦合性,因为静态库中是不可以包含其他静态库的,使用的时候要另外导入它的依赖库,最大限度的保证了每一个静态库都是独立的,不会重复引用。
4、静态库有.a 和 .framework两种形式。动态库:
1、iOS平时使用的系统库基本是动态库,比如使用频率最高的UIKit.framework和Fundation.framework。
2、动态库在程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。
3、动态库在制作的时候可以直接包含静态库,也能自动link所需要的依赖库。
4、动态库有.dylib/.tbd、.framework两种形式。
5、苹果禁止iOS开发中使用动态库。.a与.framework有什么区别?
1、.a是一个纯二进制文件,.a文件不能直接使用,至少要有.h文件配合,.framework文件可以直接使用。
2、.a + .h + sourceFile = .framework。
3、自己封装静态SDK建议用.framework。
一. 静态库(.a)
1、静态库(.a)的制作
- shift + command + n 创建项目,选择 Static Library
- 输入静态库名称
- 添加代码
在生成文件中写如下代码:
StaticDemo1.h文件:
#import <UIKit/UIKit.h>
@interface StaticDemo1 : NSObject
- (UIImage *)getImage1;
@end
StaticDemo1.m文件:
#import "StaticDemo1.h"
@implementation StaticDemo1
- (UIImage *)getImage1 {
UIImage *ima = [UIImage imageNamed:@"StaticDemo1.bundle/美女1.png"];
return ima;
}
@end
注意:静态库中代码引用资源推荐下面这种方式:
UIImage *ima = [UIImage imageNamed:@"StaticDemo1.bundle/美女1.png"];
- 选择需要暴露出来的.h文件,.m文件会自动编译到.a文件中
设置为public:
- 修改Build Settings中的 Build Active Architecture Only 为NO 以满足运行不同CPU环境的模拟器
- 编译
分别切换真机、模拟器,然后command + b编译,右键Show in Finder
生成的文件如下:
编译之后红色会变色白色,如下:
- 合并静态库
cd到Products目录,在终端执行如下命令:
lipo -create 静态库1路径 静态库2路径 -output 新静态库名称.a
合并Debug-iphoneos和Debug-iphonesimulator文件夹下的StaticDemo1.a文件,如下图:
合并完成后如下:
在终端使用如下命令,查看静态库支持的运行环境:
lipo -info 静态库路径
结果:
可以发现,x86_64(模拟器)和arm64(真机)都支持。
- 制作bundle
① 如果静态库中用到了图片资源,一般都放到一个bundle文件中,bundle名字一般跟 .a 或 .framework 名字一致。
② bundle的创建:新建一个文件夹,修改扩展名为 .bundle 即可,右击bundle文件,显示包内容,就可以往bundle文件中放东西。
③ 自己制作的静态库中要用到的图片资源,不建议直接拖到项目中使用,而是推荐放到bundle文件中,这样可以避免静态库的图片名和使用静态库的项目中存在的图片产生冲突。
- 完成
将上面合成后的StaticDemo1.a和下图随便一个文件夹的头文件,以及上面制作的bundle文件夹放到一起。
至此,.a静态库制作完成。
其他问题:
头文件
如果静态库需要暴露出来的 .h 比较多,可以考虑创建一个主头文件(一般主头文件和静态库同名)。
① 在主头文件中包含所有其他需要暴露出来的 .h 文件
② 使用静态库时,只需要#import 主头文件
③ 实际上苹果官方就是这么做的,例如:#import <UIKit/UIKit.h>静态库中包含了Category(分类)
如果静态库中包含了Category,有时候在使用静态库的工程中会报“方法找不到”的错误(unrecognized selector sent to instance)。
解决方案:在 Build Settings --> Other Linker Flags中加 -Objc。
2、.a静态库的使用
- 将.a、.h、资源文件拖拽到项目中
- 引入#import "StaticDemo1.h"头文件就可以使用了,结果如下:
二. 静态库(.framework)
- 静态库(.framework)和静态库(.a)的制作类似,所以下面的步骤以描述为主。
- 动态库(.framework)与静态库(.framework)的制作流程基本一样,唯一不同的是Mach-O文件的编译形式。
1、静态库(.framework)的制作
- shift + command + n 创建项目,选择Framework
- 输入静态库名称:StaticDemo2
- 添加代码
我们这次做稍微复杂一点,新建StaticDemo2Custom类,在这个类中,不但有自己的代码还使用了上面我们刚刚制作的StaticDemo1.a的代码。
① 首先导入刚才制作的StaticDemo1.a文件和.h头文件,不勾选Add to targets,如下:
② 如果你用到的第三方静态库需要依赖其他系统库的话,需要在导入第三方静态库之后再link依赖的系统库
③ 新创建类具体实现如下:
StaticDemo2Custom.h文件:
#import <UIKit/UIKit.h>
@interface StaticDemo2Custom : NSObject
- (UIImage *)customGetImage1;
- (UIImage *)customGetImage2;
@end
StaticDemo2Custom.m文件:
#import "StaticDemo2Custom.h"
#import "StaticDemo1.h"
@implementation StaticDemo2Custom
- (UIImage *)customGetImage1 {
//使用StaticDemo1.a的代码
UIImage *image = [[[StaticDemo1 alloc] init] getImage1];
return image;;
}
- (UIImage *)customGetImage2 {
//自己的代码
UIImage *ima = [UIImage imageNamed:@"StaticDemo2.bundle/美女2.png"];
return ima;
}
@end
目录结构:
- 设置Mach-O Type为Static Library,如下:
- 选择需要暴露出来的.h文件
然后需要在StaticDemo2.h(StaticDemo2.h必须放在Public里)中将你所有要公开的.h引入。
- 修改Build Settings中的 Build Active Architecture Only 为NO 以满足运行不同CPU环境的模拟器
- 分别切换真机、模拟器,然后command + b编译,右键Show in Finder
- 合并静态库
cd到Products目录,在终端执行如下命令:
lipo -create 静态库1路径 静态库2路径 -output 新静态库名称
注意:这里合并的是StaticDemo2.framework下的StaticDemo2,合并完成后将新的文件替换原来的StaticDemo2,就得到新的StaticDemo2.framework,至此静态库(.framework)制作完成。
2、静态库(.framework)的使用
- 由于我们的静态库(.framework)使用了其他静态库,所以需要导入其他静态库以及它的资源文件,如下:
- 导入头文件
#import <StaticDemo2/StaticDemo2.h>
- 编写测试代码,运行,结果如下:
可以发现,无论是静态库(.framework)的代码,还是其他静态库的代码,执行都没问题。
自动合成静态库(推荐)
上面都是手动合成静态库,很麻烦,下面介绍一下脚本合成的方法,推荐使用这种方式。
- 选中项目,点击导航栏上的Editor,选择Add Target创建一个Aggregate。
- 选中刚刚创建的Aggregate,然后选中右侧的Build Phases,点击左下方加号,选择New Run Script Phase。
- 嵌入脚本
#这个是声明生成的framework的名字,有些和工程名字一样,看你创建时候怎么写
#FMK_NAME是个变量
FMK_NAME=${PROJECT_NAME}
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${FMK_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${FMK_NAME}.framework
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
#这个是合并完成后打开对应的文件夹,你就可以直接看到文件了
open "${SRCROOT}/Products"
fi
如下:
- 模拟器和真机都编译生成framework
这一步一定不能漏掉,因为只有先生成模拟器和真机的framework之后才可以合并。
- 选中MyScript,设备选 Generic iOS Device,点击运行,如果有跳到Finder说明编译成功(运行脚本就是将模拟器和真机的framework合并成通用的framework),如下:
生成的StaticDemo2.framework就可以直接使用了。