iOS 静态库和动态库的制作

静态库和动态库
一、静态库和动态库的存在形式

静态库: .a 和 .framework

动态库: .dylib 和 .framework

静态库和动态库在使用上的区别

静态库:链接时,静态库会被完整地复制到可执行文件中, 被多次使用就有多份冗余拷贝 (左图所示)

动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。

需要注意的是:项目中如果使用了自制的动态库,不能被上传到 AppStore!

静态库和动态库中之所以都有.framework,是因为苹果自己的framework是动态库,以供我们开发者使用,但是,我们自制时,制作的是.framework的静态库。

二、什么是.a、.bundle、.framework?

在iOS中静态库以.a和.framework的形式存在,动态库以.dylib和.framework的形式存在。

之所以.framework既可能是动态库又可能是静态库,是因为苹果公司禁止用户级App使用动态库,而自己却又堂而皇之的使用动态库,这就造成了iOS中系统级的.framework是动态库,用户级的.framework是静态库。

二者区别不大,.a是纯二进制文件,.a文件不能单独使用,至少要有.h文件配合,而.framework除了二进制文件外,还包含一些资源文件(头文件,plist等),由于自身包含了头文件,所以.framework可以单独使用。

.a和.framework两种静态库,通常都是把需要用的到图片或者xib文件存放在一个bundle文件中,而该bundle文件的名字和.a或.framework的名字相同。关于bundle文件的制作方法,后面第七大点介绍。

三、静态库使用的必要性

1>. 代码的共享与重用

2>. 程序模块化

3>. 分享给别人代码库时代码隐藏

4>. 开发第三方SDK

四、编译生成静态库时,模式:debug和release的区别

静态库分为真机—Debug(调试)版本、真机—Release(发布)版本、模拟器—Debug版本、模拟器—Release版本;开发中一般都打包Release(发布)版本,将真机和模拟器版本合并,提供外界。

DEBUG,好处在于Xcode开启很多服务来监控错误,让程序员来调试用,但是耗性能。RELEASE,好处在于则相反。
所以,建议:
——在生成静态库的时候,可以把上面Run的参数调整为Release,这样节省内存,运行速度快。
——在平时用Run调试的时候,在DEBUG下运作正常后,在发布程序之前可修改Run的参数为Release,再运行一下模拟发布的情况,因为有时候在Debug下正常,在Release下会崩溃,两者对错误的敏感度不同。

五、制作.a静态库

创建工程:

01.png

在Build Settings -->link ->Other Linker Flags中(xcode 8.2)工程自动添加了-ObjC。
在使用已经制作的静态库的工程中,若是静态库中有分类,也需要加上-ObjC。

接着需要做:在copy files中添加所有的.h文件,这些.h都是暴露在外面的:

02.png

先注意检查下面Release是否为NO:Yes表示只编译选中模拟器设备对应的架构,No则为编译所有模拟器设备支持的cup架构(Debug版本同理),选择NO,然后分别在模拟器和真机下Command+B编译一下,会看到Products文件夹下的.a文件变为黑色,这个.a文件就是我们想要得到的静态库:

01.png

(亲自测:release默认为NO,debug默认为YES。debug为YES情况下,在模拟器iphone6上编译,得到的库在7P上可以用,但是在5上就会崩溃,调整debug为NO,7和5都不崩溃)

合并模拟器的.a 和真机的.a(选Generic iOS Device 进行编译即可得到真机):
  1. 通过终端打开路径/Users/**/Library/Developer/Xcode/DerivedData/,选择对应的工程文件夹,就是进入制作静态库的工程。(在工程中的.a -- show in finder, 会看到Release-iphoneos和Release-iphonesimulator文件夹,分别是真机和模拟器的.a文件)
  2. 合并真机和模拟器.a文件,在终端输入以下命令行:lipo -create 模拟器.a文件的路径 真机.a文件的路径 -output 合并后的保存路径(例:lipo -create /Users/shelin/Library/Developer/Xcode/DerivedData/StaticLib/Build/Products/Release-iphoneos/libxxx.a /Users/shelin/Library/Developer/Xcode/DerivedData/StaticLib/Build/Products/Release-iphonesimulator/libxxx.a -output /Users/shelin/Desktop/StaticLib.a)最终会在桌面得到一个合并后的StaticLib.a文件,再将暴露出来的.h头文件一起复制出来。
    3.使用:只需将.a和暴露出来的.h头文件导入工程目录下就可供外界使用。
制作的另外一种方式:

不用创建一个新的工程,在源代码的项目里,新建一个target:


01.png
02.png

在和项目并列地方有TestAProduct文件夹,把需要编译成.a的文件拖入这个文件夹里面,添加TestAProduct需要暴露的头文件,选择TestAProduct目标工程运行,则在Products里面会有生成的.a库。(TestProduct的target里面的Compoile Sources需要有所有需要的.m的引用,不然运行不会生成,拖入之后,正常的target->TestPro也是可以运行的,因为它里面的Compoile Sources有.m的引用)


03.png
六、制作.framework静态库
  1. 创建.framework的静态和动态库都是:
002.png

静态和动态库的选择在于(linking-->Mach-o Type的选择):

001.png
  1. 创建工程后,这里的Headres是自携带的:
002.png

(把.h文件都放到public里面,不然会报找不到的错误)

01.png
02.png

编译生成真机和模拟器的framework静态库的方法和.a的方法是一样的,接下来就是合成真机和模拟器的库,这里和制作.a时的合并有点不同就是:
![Upload 0001.png failed. Please try again.]

这里合并用到的是里面的wgj_makeFramework:
![Upload 002.png failed. Please try again.]

例如:lipo -create /Users/wgj/Library/Developer/Xcode/DerivedData/wgj_makeFramework-gevmzbcgwfblmsewnayqsoutdztu/Build/Products/Debug-iphoneos/wgj_makeFramework.framework/wgj_makeFramework /Users/wgj/Library/Developer/Xcode/DerivedData/wgj_makeFramework-gevmzbcgwfblmsewnayqsoutdztu/Build/Products/Debug-iphonesimulator/wgj_makeFramework.framework/wgj_makeFramework -output /Users/wgj/Desktop/newFramework
其中,output后面的newFramework不是提前建好的文件夹,生成之前,桌面上不需要做操作。
这样,生成后,直接把桌面的生成的东东替换库里面的最后层次的wgj_makeFramework就好了,newFramework名字最好改为wgj_makeFramework。

七、制作.bundle

1、bundle资源库的特点

Bundle是静态的,也就是说,我们包含到包中的资源文件作为一个资源包是不参加项目编译的。也就意味着,bundle包中不能包含可执行的文件。它仅仅是作为资源,被解析成为特定的2进制数据

  1. 创建


    01.png

3、加入你需要编译在bundle中的资源文件(若是图片,需带后缀.png)。

当然,默认的配置也是可以的,如果你需要特定的优化或者特定的路径配置,你可以进行下面第4步的配置。

  1. 进行可选设置
    资源包只需要编译不需要安装:所以设置skip install 为YES;然后删除安装路径如下:


    02.png
  2. 编译即可得到.bundle文件
    6.导入项目应用,调用方式,例如图片的三种加载的方式:
    //1.
    // imageV.image = [UIImage imageNamed:@"wgj_bundle.bundle/Contents/Resources/0001.png"];
    //2.
    // NSString *fileS = [[NSBundle mainBundle] pathForResource:@"wgj_bundle.bundle/Contents/Resources/0001" ofType:@"png"];
    // imageV.image = [UIImage imageWithContentsOfFile:fileS];
    //3.
    NSString *path = [[NSBundle mainBundle] pathForResource:@"wgj_bundle" ofType:@"bundle"];
    NSBundle *bundle = [NSBundle bundleWithPath:path];
    NSString *file = [bundle pathForResource:@"0001" ofType:@"png"];
    imageV.image = [UIImage imageWithContentsOfFile:file];
    注意:在制作静态库时,若把静态库里面的图片(正常的图片,不是bundle里面的)暴露出来,则静态库工程和用到静态库的工程可以直接用,[UIImage imageNamed:@"has.png"];如果是不暴露出来,则静态库工程和用到静态库的工程都用不了(静态库工程用了无效)。若是在制作静态库的时候,用.bundle文件封装图片资源,静态库制作时,把.bundle文件暴露在外界,则也是一种方法,在外界通过路径加载也是可以使用到图片的(.bundle文件不暴露在外界,图片在静态库中,也是使用无效的)
    (资源文件都打包放在这里。在制作Framework的时候不可以把图片直接放在项目中,否则制作好之后图片是一张一张的出现在项目中非常乱,需要新建一个bundle将图片放进去,这里的bundle提供整个SDK的图片资源。
    注意:图片放进bundle之后不可以用[UIImage ImageWithName:]读取图片。要先找到bundle包再拿图片)
八、制作.framework动态库

制作.framework的动态库和制作.framework的静态库基本相同,其不同点:

01.png

制作库时这个地方改为:Dynamic Library,另外在使用已制作动态库的 工程中添加:

02.png

这个图片中的Embedded Binaries 里面的库是自制的动态库,需要手动添加。
当然在使用静态库时,需要把这个Embedded Binaries 中的已手动添加的动态库去掉。(在导入自己制作的动态库时,需要在Embedded Binaries中导入,不然会报错:image not found。此时这个动态库会跟静态库一样被拷贝到目标程序中进行编译,苹果又把这种Framework叫做Embedded Framework)

推荐阅读更多精彩内容