前言:当开发到了一定程度后,积累的工具类或者封装的东西就越来越多,而很多工程会重复使用到这些类,但是又不想每个工程都导入一遍,可能会想到将这些打包成(.a)库或者bundle,但是这样子有变动的时候又得重新编译一遍,再导入,耗费不少时间。解决的方法很多,比如建立workspace,多个工程同级使用等。写这文章,是为了记录以便日后翻看。
1、静态库(.a)库
在iOS中静态库以.a和.framework的形式存在,动态库以.dylib和.framework的形式存在。之所以.framework既可能是动态库又可能是静态库,是因为苹果公司禁止用户级App使用动态库,而自己却又堂而皇之的使用动态库,这就造成了iOS中系统级的.framework是动态库,用户级的.framework是静态库。二者区别不大,.a是纯二进制文件,.a文件不能单独使用,至少要有.h文件配合,而.framework除了二进制文件外,还包含一些资源文件(头文件,plist等),由于自身包含了头文件,所以.framework可以单独使用。
静态库创建好了,如果现在编译的话,就能得到(.a)库了。
这里需要注意的是,文件夹“include”里边包含的是类的头文件,当你发现添加了依赖,却找不到某个类的时候,去Build Phases->CopyFiles查看一下是否有添加了该类的头文件
图中的第四步是默认的头文件build后存放的位置,可以将它改变(比如:../$(PRODUCT_NAME)/Headers),以便在其它工程中建立依赖时容易找到,这个用处后面会有说到。
而(.a)库build后的存放位置也是可以改变的,例:
之所以改变它们的编译路径,就是为了之后的建立依赖方便找到,和改变头文件是一样的。
需要注意的是,build后的存放分为debug,release,还有模拟器,改变存放路径是最好分开。
特别需要注意的是,在Build Settings里边有个叫Build Active Architecture Only的属性,为了编译速度快,Debug情况下默认为Yes,只是编译当前版本,在这里我们需要设置成NO,以满足我们在不同的环境中使用,当碰到静态库引用报错时,我们除了可以检查路径是否正确外,还可以利用命令行“lipo -info”来查看当前引用的库所支持的环境。
到了这里,(.a)库的创建和简单配置基本完成,根据不同环境编译后,可以导入到其它工程使用了。但,这只是简单使用,显然不能满足我们前面说的,现在,接着走。
使用过(.a)库的人知道,假如我们使用的工具类带了(.xib)呢?你会发现,编译的时候,没有见到xib的文件
(.xib)文件没有关联上去,怎么办呢?
2、Bundle(实际就是一个文件夹)
根据上面说到的可以得出,静态库(.a)库没有bundle的存在,无法使用(.xib)文件,根据平时正常工程中使用本地文件例如(.xib)需要用到NSBundle外,本地的图片或者(.txt)等也是需要用到,这些文件在静态库中,都需要放到创建的bundle中,这样,编译的时候才能编译到。
因为IOS那类没有Bundle选择,故选择macOS里边的,创建后,需要设置一下Build Settings里边的Base SDK
然后Bundle的build的路径也参照前面的设置,将其改变。
设置在编译主target(.a)库的前面,先编译Bundle
然后选中刚才的(.xib),勾上target关联到Bundle,再编译
注:如果静态库中不需要使用到(.xib)或者图片等文件时,Bundle不是必须的。
3、关联工程
静态库和Bundle都创建好,并且配置编译通过后,接下来就是如何关联到需要用到的工程中去了
首先,在其它工程我们需要用到(.a)库,但是我们不想像一般情况那样子直接将它拖到工程中去,弊端上面有说到。所以需要将主工程与静态库建立依赖:
-
将主工程和静态库工程放到同一目录下(方便路径关联)
-
将编译后的Bundle用引用的方式导入主工程(因为Bundle内的文件不会根据编译环境变化,比较固定,直接引用)
设置动态依赖关联静态库(Build Settings -> Library Search Paths)
Debug:$(SRCROOT)/../ToolProject/Build/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
Release:$(SRCROOT)/../ToolProject/Build/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
- Other Linker Flags(重点介绍一下)
在使用第三方静态库的时候,一般会提醒在Other Linker Flags里边配置“-ObjC”或者“-all_load”或者“-force_load”,它们的作用是:
- -ObjC
告诉链接器将库中的Objective-C类和Category类都加载进来(命名不重复的类),但是这样子做有个弊端。没有用到的类也都加载进来了,APP会变大。而且当库中只存在Category类时,“-ObjC”就不起作用了- -all_load
强制将所有类都加载进来,能解决只存在Category类的情况。但是,引用多个静态库时,它们之中可能存在重命名的类别,这样子会有问题- -force_load
它所做的事情和“-all_load”差不多,但是它能指定完全加载某个静态库,而不影响其它的库。
- 所以建议“-ObjC”和“-force_load”结合使用
- 上面说过,静态库使用需要结合头文件,所以需要设置搜索头文件的路径
$(SRCROOT)/../ToolProject/Build/ToolProject/Headers/
注:以上静态库或者头文件路径的设置都是看实际存放的路径的,前面一直强调更改它们的路径就是这个原因。
-
到这里基本配置就完成了,你会发现在主工程中也可以使用依赖库中的东西了。
4、脚本的配置
完成了上面的操作后是可以正常使用了,但是,当你clean了静态库后,你会发现,你得重新Build一下,否则无法直接运行主工程,原因,应该大家都懂,那有什么办法解决呢
-
Build Phase -> New Run Script Phase(要将Run Script放到Compile Sources 的编译前,原因是编译主工程前,需先编译最新的静态库)
脚本代码
#!/bin/bash
#进入脚本目录
BASEDIR=$(dirname $0)
cd $BASEDIR
build_cmd='xcodebuild OTHER_CFLAGS="-fembed-bitcode" -target ToolProject -configuration '${CONFIGURATION}' -sdk '${SDK_NAME}
echo '执行build命令:'${build_cmd}
${build_cmd}
至此,基本介绍完静态库和Bundle的创建和使用。其中的不足和错漏,恳请斧正。