创建framework静态库

第一步,新建项目



ios 静态库引用 ios 静态库 动态库_ios 静态库引用

新建项目.png



第二步,删除系统默认创建的【FMDB.h】和【FMDB.m】文件,导入需要打包的源文件。




ios 静态库引用 ios 静态库 动态库_xcode_02

导入源码后的工程.png



第三步,修改项目配置
         首先,设置需要暴漏的头文件




ios 静态库引用 ios 静态库 动态库_二进制文件_03

header文件设置.png



这里需要注意的是暴露出来的头文件中import的其他类也得添加到public中暴露出来。
如果不想将import的类暴露出来,那么在头文件中用@class 然后在对应的.m文件中再import。

然后设置编译模式,在Xcode菜单【Product】--->【Scheme】--->【Edit Scheme...】中




ios 静态库引用 ios 静态库 动态库_静态库_04

设置编译模式.png



设置编译出的静态库包含的指令集




ios 静态库引用 ios 静态库 动态库_二进制文件_05

设置编译出的静态库包含的指令集.png



最后修改生成的Mach-O格式




ios 静态库引用 ios 静态库 动态库_二进制文件_06

修改Mach-O 格式.png



第四步,编译生成静态库
         编译时,需要用模拟器和真机各编译一次,这样Products目录下的libFMDB.a静态库才会变为黑色,右键show in Finder,可以进入Products目录下。




ios 静态库引用 ios 静态库 动态库_bundle_07

编译生成的framework静态库.png



第五步,合并模拟器版framework和真机版framework
           合并的命令同上面相似,不同之处是:framework静态库合并的不是framework,而是framework下的一个二进制文件,即上一步图中标记的文件。
           



lipo -create 第一个framework下二进制文件的绝对路径 第二个framework下二进制文件的绝对路径 -output 最终的二进制文件路径。



本文中使用的命令如下:



lipo 
    -create 
       /Users/harvey/Library/Developer/Xcode/DerivedData/FMDB-clvayfrjgytqrbdkyqrtcjkxfeuz/Build/Products/Release-iphonesimulator/FMDB.framework/FMDB 
       /Users/harvey/Library/Developer/Xcode/DerivedData/FMDB-clvayfrjgytqrbdkyqrtcjkxfeuz/Build/Products/Release-iphoneos/Release-iphoneos.framework/FMDB
    -output 
       /Users/harvey/Desktop/FMDB



最后将任何一个framework中的二进制文件替换成合并后的二进制文件即可。
把framework添加到要使用的项目中即可使用。

注意:
    如果创建的framework中使用了category类,则在使用framework的项目配置中【Other Linker Flags】需要添加参数【-ObjC]或者【-all_load】。
    如果使用framework的使用出现【Umbrella header for module 'XXXX' does not include header 'XXXXX.h'】,是因为错把xxxxx.h拖到了public中。
    如果出现【dyld: Library not loaded:XXXXXX】,是因为打包的framework版本太高。比如打包framework时,选择的是iOS 9.0,而实际的工程环境是iOS 8开始的。

如果创建的framework类中使用了.dylib或者.tbd,首先需要在实际项目中导入.dylib或者.tbd动态库,然后需要设置【Allow Non-modular Includes ....】为YES,否则会报错"Include of non-modular header inside framework module"。




ios 静态库引用 ios 静态库 动态库_bundle_08

设置【Allow Non-modular Includes ....】.png



补充:
    打包成的静态库肯定是比源码类要大很多的,因为是由不同指令集不同设备的版本合并成的。所以如果你很在意你的app大小,并且也不是很需要打包成静态库的话,还是用原始类吧。
   framework静态库中是可以包含图片资源的;而.a静态库中不能包含图片资源,只能另外创建一个目录存放。

填坑记录

上面的注意里提到了一些坑,以及解决办法。这里再记录一些:
1.framework中用到了NSClassFromString,但是转换出来的class 一直为nil。
   先来看一下这个API的官方描述




ios 静态库引用 ios 静态库 动态库_ios 静态库引用_09

官方描述.png



     什么意思呢?
          如果转换出来的class为nil,有两种情况:
              第一种情况是这个类不存在;
              第二种情况是这个类还没有被load。所以一般出现问题,都是第二种情况。
     怎么解决这个问题呢?
          在主工程的【Other Linker Flags】需要添加参数【-ObjC]即可。

2.framework中把图片、音频打包进bundle中,但是一直加载不到。
   打包的framework中有一个bundle,bundle里有一些图片、音频等资源。但是用如下方式:



NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"XXXX" ofType:@"bundle"];
NSString *mp3Path = [[NSBundle bundleWithPath:bundlePath] pathForResource:@"Message_system" ofType:@"mp3"];



   获取的mp3Path一直为nil。framework,只暴露了一些.h头文件,bundle没有暴露出来,无法获取。那么我们只需要将bundle与framework一起放入目标工程中即可。其实bundle根本不用打包进framework中。
例如:
   我们创建了一个叫ABC.framework的静态库。库中使用了Message_system.mp3,那么我们创建一个bundle,命名为ABC.bundle,然后将Message_system.mp3放入bundle中。打包的时候,framework并不包含ABC.bundle。最后在要使用ABC.framework的工程中,新建一个文件夹or group,然后把ABC.framework和ABC.bundle一起拖进去,就可以啦。