最近想把自己的一些实用工具类搞成一个静态库,网上搜了下关于framework和.a的一些相关资料,然而写的或不全面,或不详细,我归纳总结及亲自实践写下这篇文章。
一、framework和.a两种静态库的介绍及区别
.a是一个纯二进制文件,.framework中除了有二进制文件之外还有资源文件。
.a文件不能直接使用,至少要有.h文件配合,.framework文件可以直接使用。
.a + .h + sourceFile = .framework。
.a只是静态库。framework既可以是静态库也可以是动态库。例如系统的framework就是动态库。
静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。
动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。
然而苹果是不会让开发者有自己的动态库的。
二、framework制作及使用
1.新建framework项目
2.加一些我们实用的类进来
3.文件刚拉进来时如下图,只有APPBaseSDK.h是默认放在public中,我们还要把project中需要暴露给外面用的.h文件移到public中去
4.然后设置编译模式,打开Xcode菜单Product--->Scheme--->Edit Scheme,改为release模式,因为最终打包是要用release模式
5.设置最低支持版本
6.设置编译出的静态库包含的指令集
模拟器:iPhone4s~5 : i386 iPhone5s~6plus : x86_64
真机:iPhone3gs~4s : armv7 iPhone5~5c : armv7s iPhone5s~6plus : arm64
如果Build Active Architecture Only设置为YES,那么编译出来的静态库就只包含当前设备的指令集。
举个例子:如果我们选择iPhone 5模拟器编译,则编译出来的静态库只能用iPhone4s~5模拟器跑程序,用iPhone5s~6plus,则会报找不到x86_64的APPBaseSDK库。
设置为NO,则会把所有指令集的都打包合并。因此静态库有个缺点就是静态库包比源码大很多。
7.最后修改生成的Mach-O格式
8.编译生成静态库
编译时,需要用模拟器和真机各编译一次,这样Products目录下的APPBaseSDK.framework静态库才会变为黑色,右键show in Finder,可以进入Products目录下。
9.合并静态库文件
要让真机和模拟器都可用该静态库,需要将两种静态库合并。framework静态库合并的不是framework,而是framework下的一个二进制文件,即上图中标记的待合并文件。lipo -create 第一个framework下二进制文件的绝对路径 第二个framework下二进制文件的绝对路径 -output 最终生成合并的二进制文件路径(我把它放桌面上)。
打开终端使用的命令如下:
lipo -create /Users/zhanglinfeng/Library/Developer/Xcode/DerivedData/APPBaseSDK-dpqdspcdgwsrxgdihiaxpqpkvali/Build/Products/Release-iphoneos/APPBaseSDK.framework/APPBaseSDK /Users/zhanglinfeng/Library/Developer/Xcode/DerivedData/APPBaseSDK-dpqdspcdgwsrxgdihiaxpqpkvali/Build/Products/Release-iphonesimulator/APPBaseSDK.framework/APPBaseSDK -output /Users/zhanglinfeng/Desktop/APPBaseSDK
如果觉得敲文件路径好麻烦,可以将该文件拖入终端即键入了该文件的路径。如果报错建议将上图中Release-iphoneos和Release-iphonesimulator中的APPBaseSDK.framework删掉,重新用模拟器和真机分别编译一次再试。
真机的或者模拟器中随便选一个framework,将该framework中的二进制文件APPBaseSDK(也就是上图中待合并的文件)替换成刚刚合并的文件(刚生成在桌面上的)。好了,此framework就是我们需要的。
10.使用framework
将framework拖入新建的一个UseFrameworkTest工程,我这里拖到下图箭头所指的UseFrameworkTest文件夹目录下,如果你要拖到其他目录,就要改Library Search Paths,后面我会介绍Library Search Paths路径相关知识
将framework中比较常用的头文件import到APPBaseSDK.h中(如下图画圈的),这样在外面只需要#import <APPBaseSDK/APPBaseSDK.h>,就可以引用到下图中画圈的文件。如果不想将太多文件都import到APPBaseSDK.h中,在外面就像这样引用#import <APPBaseSDK/MBProgressHUD+Easy.h>(虽然能使用但会报警告,所以还是import到APPBaseSDK.h中吧)
使用代码,如下图可以正常调用将汉字转拼音方法并打印结果了
注意:如果要用到framework中的category方法,需要设置Other Linker Flags为-ObjC(注意大小写,有些资料里大小写搞错了坑死我了)。引入了-ObjC标志,它的作用就是将静态库中所有的和对象相关的文件都加载进来本来这样就可以解决问题了,不过在64位的Mac系统或者iOS系统下,链接器有一个 bug,会导致只包含有类别的静态库无法使用-ObjC标志来加载文件。变通方法是使用-all_load 或者-force_load标志,它们的作用都是加载静态库中所有文件,不过all_load作用于所有的库,而-force_load后面必须要指定具 体的文件。
三、.a静态库的制作及使用
1.创建静态库工程,工程命名为BaseSDK,生成的.a文件名变成libBaseSDK。
2.删掉自动生成的文件BaseSDK.h BaseSDK.m
3.添加你的实用类文件
4.添加Headers Phase
5.将暴露给外面用的头文件加入进来,加进来后要移到public中去(不移到public中也没错,只是下面第11步中不会出现.h文件,需要从库的源码中找)
6.然后设置编译模式,打开Xcode菜单Product--->Scheme--->Edit Scheme,改为release模式,因为最终打包是要用release模式
7.设置Build Active Architecture Only
模拟器:iPhone4s~5 : i386 iPhone5s~6plus : x86_64
真机:iPhone3gs~4s : armv7 iPhone5~5c : armv7s iPhone5s~6plus : arm64
如果Build Active Architecture Only设置为YES,那么编译出来的.a静态库就只包含当前设备的指令集。
举个例子:如果我们选择iPhone 5模拟器编译,则编译出来的.a静态库只能用iPhone4s~5模拟器跑程序,用iPhone5s~6plus,则会报找不到x86_64的APPBaseSDK库。
设置为NO,则会把所有指令集的都打包合并。因此静态库有个缺点就是静态库包比源码大很多。
8.设置最低支持版本
9.编译生成静态库
编译时,需要用模拟器和真机各编译一次,这样Products目录下的libBaseSDK.a静态库才会变为黑色,右键show in Finder,可以进入Products目录下。
10.合并模拟器和真机静态库文件libBaseSDK.a,打开终端命令如下
lipo -create /Users/zhanglinfeng/Library/Developer/Xcode/DerivedData/BaseSDK-cexmrzesjuswutaldkedwjpnpxnk/Build/Products/Release-iphoneos/libBaseSDK.a /Users/zhanglinfeng/Library/Developer/Xcode/DerivedData/BaseSDK-cexmrzesjuswutaldkedwjpnpxnk/Build/Products/Release-iphonesimulator/libBaseSDK.a -output /Users/zhanglinfeng/Desktop/libBaseSDK.a
11.使用.a静态库
将静态库拖入新建的工程,我这里拖到下图箭头所指的UseA文件夹下(如果你要拖到其他目录,就要改Library Search Paths,后面我会介绍Library Search Paths路径相关知识),再将暴露给外面用的.h文件也拖入工程,
注意:如果没有include里的.h文件.那就从库的源码中挑出一些需要暴露的.h文件。
导入头文件就可以使用了,如下图
注意:如果要用到静态库中的category方法,需要设置Other Linker Flags为-ObjC(注 意大小写,有些资料里大小写搞错了坑死我了)。引入了-ObjC标志,它的作用就是将静态库中所有的和对象相关的文件都加载进来本来这样就可以解决问题 了,不过在64位的Mac系统或者iOS系统下,链接器有一个 bug,会导致只包含有类别的静态库无法使用-ObjC标志来加载文件。变通方法是使用-all_load 或者-force_load标志,它们的作用都是加载静态库中所有文件,不过all_load作用于所有的库,而-force_load后面必须要指定具 体的文件。
四、关于使用库时Library Search Paths
也许有人会纳闷不用设置search Paths吗,如果你按照我的步骤把静态库拖到工程的相应目录,那就默认设置就可以了。
如果你把静态库放到你喜欢的路径,就要根据静态库在文件夹中的位置设置这个search Paths了。
下图标明了文件夹路径
“./”和“$(PROJECT_DIR)”表示当前工程所在文件夹,是一个相对路径,相对该工程在电脑的位置,会自动定位到在当前电脑的绝对路径。如果写绝对路径,工程换了文件夹位置,或换到其他电脑,路径就报错。“../”表示当前工程所在文件夹的上一层文件夹路径。设置路径时两个参数的意义,non-recursive非递归查找,recursive 递归查找 。