网上有很多教程可以参考,但是还是有一些问题的解决方法不正确。现在这里重新介绍一下。制作.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)。
|
|
|
|
再修改一下工程的一些配置,网上的说法是让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,里面有三个选项,Public
, private
, Project
。代表头文件是否暴露。但其实没看出有什么区别。
重要的是在上面的Copy Files
这一项,这个代表了将哪些头文件拷贝出去指定目录。如果你是使用工程嵌套的方法,外部工程会将库工程生成的.a和include文件夹拷贝到它的输出目录的。所以这里决定了外部工程可以看到哪些头文件。你会发现我们后面拖进去的UIImage2OpenCV.h
并不在里面,点加号,把它添加上去。
另外需要注意的一点是这里上面的Compile Sources
这一项,一般来讲,你拖到工程里面的源文件都会被添加到这里的,但是也出现过意外,如果你编译出来的库在使用的时候报了在xxx架构下找不到XXX定义,请回来看看这里是否将所有源文件添加进来了。
好了,选择模拟器,编译一下,再选择真机,编译一下。然后打开工程的Products
目录,你会看到模拟器和真机的输入,如果你需要这个库在模拟器和真机环境下都可以运行,那么需要合并这两个.a文件。命令如下
|
|
前面两个xxx代表了两个.a文件的路径,后面的newName代表生成的.a文件的路径。运行后就可以得到一个总的.a文件,可以使用下面的命令来看这个.a文件支持的架构。
|
|
如果合并成功,应该可以看到.a文件支持armv7 armv7s i386 x86_64 arm64这些架构。
使用的时候将.a文件和include里面的头文件拷贝过去就行了。一般的.a文件这样就行了,但是我们的.a文件你直接用肯定不行的。这里有两个问题:
- 我们用到了opencv的framework。
- 代码里面有类别。
使用framework编译的.a文件,你导入时候会发现找不到opencv的头文件。所以我们仍需要在使用的工程里面导入opencv的framework。
第二个是使用了类别的问题,就是UIImage2OpenCV
这个文件。猜测是由于OC的动态加载机制,没有使用到的类是不会加载的。所以.a文件里面的类别没有加载,直到使用的时候也是,所以就会出现运行时报出找不到UIImage toMat
的错误。网上推荐的方法是在静态库的工程设置里面将other linker flags
设置为-Objc
和-all_load
,但是实际测试发现,在64位机器上根本不行。仍然报错,有些网上说的是将上面的设置用在使用到.a文件的工程里面,但是你会发现这样改直接导致编译错误。报错是opencv的一些符合在该架构下找不到。
解决的方法有两个:
- 不是没加载文件导致的吗,那我就强制加载一下。在类别文件里面再定义一个类,然后在使用的工程里面先创建一下这个类的对象,那么OC就会将这个文件加载起来了,里面的类别方法自然可以使用。但是感觉这个方法好笨,不旦要新建一个多余的类,而且还要创建多余的对象。
- 最后找到的方法是:在使用.a文件的工程的设置里面,将
other linker flags
设置为-force_load xxx.a
,将xxx.a替换为你的.a文件。这样就没问题了。