Ekulelu's Blog

iOS静态库.a文件的制作和使用

网上有很多教程可以参考,但是还是有一些问题的解决方法不正确。现在这里重新介绍一下。制作.a文件的过程是在移植AR库的时候碰到的,这里还使用了opencv的framework,所以还有一些使用上的问题要留意。

下面介绍.a的制作方法:

首先新建一个工程,选择Static Library。

创建工程之后我们得到了一个这样的工程目录,总的来讲,这个和工程名字一样的.h文件,就是我们以后使用的库的主要头文件。一般的做法是在它里面引用你库里面的其他头文件,然后使用者只要引用这个头文件就可以引用到库的其他头文件了。这里有个注意的地方,待会儿再讲。

因为我们需要用到opencv的framework,所以把它拖到工程里面,这个时候有一个拖文件的时候是否copy is need的选项,这个最好把它勾选,让xcode把这个framework拷贝到工程的目录中去,否则你需要在Build Settings里面的Framework Search Paths里面添加opencv的framework所在的目录。

这里顺便添加一个将UIImage转换为Mat的类别进工程。然后工程目录如下:

这里简单的在StaticLib.h/mm里面添加一个方法。(留意这个.mm文件,因为需要用C++的代码,所以需要改文件后缀为.mm)。

1
2
3
4
5
6
7
//StaticLib.h
#import <opencv2/opencv.hpp>
#import <UIKit/UIKit.h>
#import "UIImage2OpenCV.h"
@interface StaticLib : NSObject
- (cv::Mat)dealWithImage:(UIImage*)image;
@end
1
2
3
4
5
6
7
//StaticLib.mm
#import "StaticLib.h"
@implementation StaticLib
- (cv::Mat)dealWithImage:(UIImage*)image {
return [image toMat]; //toMat是UIImage2OpenCV的类别方法。
}
@end

再修改一下工程的一些配置,网上的说法是让xcode不要移除没有使用的代码。

Dead Code Stripping - Set this to NO

Strip Debug Symbols During Copy - Set this to NO for all configurations

Strip Style - Set this to Non-Global Symbols

另外需要修改的是编译架构,在Build settings里面,xcode8是默认不包含armv7s架构的,这个架构在iPhone 5,iPad4里面用到了,所以最好添加上去吧,另外一个就是是否只编译当前架构,这个将所有改为NO,否则只会编译出当前使用的架构。

然后到工程的Build Phase里面,点击Xcode的菜单的Editor,选择里面的Add Header Build Phase,然后Build Phase里面会多一项Header,里面有三个选项,PublicprivateProject。代表头文件是否暴露。但其实没看出有什么区别。

重要的是在上面的Copy Files这一项,这个代表了将哪些头文件拷贝出去指定目录。如果你是使用工程嵌套的方法,外部工程会将库工程生成的.a和include文件夹拷贝到它的输出目录的。所以这里决定了外部工程可以看到哪些头文件。你会发现我们后面拖进去的UIImage2OpenCV.h并不在里面,点加号,把它添加上去。

另外需要注意的一点是这里上面的Compile Sources这一项,一般来讲,你拖到工程里面的源文件都会被添加到这里的,但是也出现过意外,如果你编译出来的库在使用的时候报了在xxx架构下找不到XXX定义,请回来看看这里是否将所有源文件添加进来了。

好了,选择模拟器,编译一下,再选择真机,编译一下。然后打开工程的Products目录,你会看到模拟器和真机的输入,如果你需要这个库在模拟器和真机环境下都可以运行,那么需要合并这两个.a文件。命令如下

1
lipo -create xxx xxx -output newName

前面两个xxx代表了两个.a文件的路径,后面的newName代表生成的.a文件的路径。运行后就可以得到一个总的.a文件,可以使用下面的命令来看这个.a文件支持的架构。

1
lipo -info xxx.a

如果合并成功,应该可以看到.a文件支持armv7 armv7s i386 x86_64 arm64这些架构。

使用的时候将.a文件和include里面的头文件拷贝过去就行了。一般的.a文件这样就行了,但是我们的.a文件你直接用肯定不行的。这里有两个问题:

  1. 我们用到了opencv的framework。
  2. 代码里面有类别。

使用framework编译的.a文件,你导入时候会发现找不到opencv的头文件。所以我们仍需要在使用的工程里面导入opencv的framework。
第二个是使用了类别的问题,就是UIImage2OpenCV这个文件。猜测是由于OC的动态加载机制,没有使用到的类是不会加载的。所以.a文件里面的类别没有加载,直到使用的时候也是,所以就会出现运行时报出找不到UIImage toMat的错误。网上推荐的方法是在静态库的工程设置里面将other linker flags设置为-Objc-all_load,但是实际测试发现,在64位机器上根本不行。仍然报错,有些网上说的是将上面的设置用在使用到.a文件的工程里面,但是你会发现这样改直接导致编译错误。报错是opencv的一些符合在该架构下找不到。
解决的方法有两个:

  1. 不是没加载文件导致的吗,那我就强制加载一下。在类别文件里面再定义一个类,然后在使用的工程里面先创建一下这个类的对象,那么OC就会将这个文件加载起来了,里面的类别方法自然可以使用。但是感觉这个方法好笨,不旦要新建一个多余的类,而且还要创建多余的对象。
  2. 最后找到的方法是:在使用.a文件的工程的设置里面,将other linker flags设置为-force_load xxx.a,将xxx.a替换为你的.a文件。这样就没问题了。