Library介绍
基本知识
在实际的编程过程中,通常会把一些公用函数制成函数库,供其它程序使用,一则提搞了代码的复用;二则提搞了核心技术的保密程度。
Library使用的两种方式:封装lib.a和直接引用lib工程。
一、封装.a文件
直接封装lib.a,向使用者提供头文件列表。使用者引用头文件并且使用其中方法,但是看不到实现文件的内容。这种方式每当静态库函数需要修改时就必须重新生成lib.a提供给使用者更换,比较麻烦,但有助于保密。
制作静态库
New Project -> iOS Library ->Cocoa touch Static Library 这样就新建了一个静态库的工程,将你要打包成lib的.m,.h放到class目录下面,然后选择build就可以了.
Bulid之后,在工程目录下Produces文件夹下可以看到生成的.a文件引用,右键,show in finder可以看到.a文件。
要注意Build时的选项:
<1>iOS Device编译出来的是在Debug-iphoneos目录下,真机使用,终端,在该目录下使用lipo -info **.a 可以查看你到文件类型为armv7等ARM架构。
<2>Simulator时编译出来的是在Debug-iphonesimulator目录下,模拟器使用,终端查看类型显示为i386架构。
可以使用lipo命令生成一个通用二进制lib.a lipo -create **/**.a **/**.a -output **/**.a 生成一个兼容两种类型的.a文件。方法虽好,但是包大小会增加。
<3>.a文件所在木有没有include文件夹,如何设置?
在项目Target设置页面选择Build Phases,然后选中里面的某一项(必须选择一下,否则后面的操作不能进行),然后菜单栏Editor->add Build Phases->Add Copy Build Phases即可生成Copy Files,在里面配置生成的路径及需要生成的头文件,选择Product Directory,路径例如:include/$(PRODUCT_NAME),然后Clean-Build即可发现.a文件所在目录多了一个include目录,包含了配置好的头文件。
使用静态库
在需要调用静态库的工程的目录下通过右键点 Frameworks->Add->Existing Files..添加之前创建的.a静态库文件,然后在需要调用静态库的函数的文件里,import进来静态库中.h头文件,这样就可以使用静态库里的函数了。(此处可以做一个头文件包含静态库中所有的头文件,只需声明这一个头文件就可以使用所有的相应头文件的方法)
问题及注意事项
0. .a文件路径:/Users/user/Library/Developer/Xcode/DerivedData/****/Build/Products/
不同模式下可以生成不同类型的.a文件 真机/模拟器与Debug/Release选项公交叉成4种.a文件。
1.打包分清楚是debug与Release的。
选择debug与Release在Xcode工具栏的Product选项现则Scheme->Edit Scheme.然后为各个运行模式选择选项。
2.分清楚lib是i386(真机)或者ArmV7(模拟器)模式
终端下使用命令 lipo -info libPrint.a 可以查看.a的属性。如结果:libPrint.a is architecture(构建): armv7
3.把真机运行和模拟器运行的.a文件合并生成通用的.a文件,完成通用的静态库。
终端使用命令 lipo -create 真机.a路径 模拟器.a路径 -output 目标路径(如/users/user/desktop/***.a)。然后info查看合并后.a的信息就会发现它已经同时具备了armv7和i386的条件
4.在Build Phases->Compile Source中的文件,表示这些代码会被编译进lib中,你可以删掉你不希望被编译的。
5.标准的Unix引入惯例是一个include文件夹,用来存放所有引用的外部头文件,一个lib文件夹用来存放库文件(.a)。这种文件夹结构这是一种惯例,并不强制。
附:自动生成通用lib.a
生成通用二进制lib.a需要lipo,一个命令行工具,它允许在通用文件上执行操作(类似于创建通用二进制, 列出通用文件内容等等)。本教程中使用lipo的目的是联合不同架构的二进制文件到单个输出文件中。你可以直接在命令行中使用lipo命令,但在本教程中你可以让Xcode执行一段创建通用库的命令行脚本来为你做这件事。
Xcode中一个集合目标可以一次构建多个目标,包括命令行脚本。在选中lib工程文件,点击+号增加新的Target,选择iOS/Other并点击Aggregate,如下图:
将目标命名为UniversalLib,确保选中你的lib工程中。然后选择UniversalLib Target。切换到Build Phases标签;点击+号增加Add Run Script Build Phase,如下图:
现在你需要设置脚本项。展开Run Script模块,在Shell行下粘贴如下代码:
# define output folder environment variable
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
TARGET_NAME=ProjectName
# Step 1. Build Device and Simulator versions
xcodebuild -target ${TARGET_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
xcodebuild -target ${TARGET_NAME} -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step 2. Create universal binary file using lipo
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a"
# Last touch. copy the header files. Just for convenience
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_OUTPUTFOLDER}/"
注意:修改其中的TARGET_NAME=ProjectName为你的lib工程名。
代码并不十分复杂,它是这样工作的:
UNIVERSAL_OUTPUTFOLDER 包括了通用二进制包将要被存放的文件夹:“Debug-universal”
Step 1. 第2行执行了xcodebuild并命令它构建ARM架构的二进制文件。
下一行再次执行了xcodebuild命令并在另一个文件夹中构建了一个针对Inter架构的iPhone模拟器的二进制文件,在这里关键参数是-sdk iphonesimulator -arch i386。
Step 2. 现在已经有了2个.a文件分别对应两个架构。执行lipo -create,用它们创建出一个通用二进制。
最后一行的作用是复制头文件到通用构建文件夹的外层。
现在你已经准备好构建一个静态库的通用版本。
选择UniversalLib然后Run,你就会在产品目录发现一个新的文件夹Debug-Universal或者Release-Universal,里面包含了合并之后的lib.a以及头文件。
详细操作参考链接:http://www.cocoachina.com/applenews/devnews/2013/1204/7468.html
二、引用lib工程
静态库工程被包含在项目工程中或者与项目工程放在同一个WorkSpace中,做成联调静态库。这种方式的静态库工程与项目工程一起使用,故没有对Libray中的代码进行封装,可以查看修改。
创建联调工程
1.在工程的Targets上右键.Add -> New Target -> Static Library 比如我们建了一个LibExample的target。这样是一个工程包含多个Target的形式,没有新建Lib工程。创建好Target之后你会发现原来的工程下面会多出几个文件夹:LibExample和LibExampleTest,用来存放跟Library相关的代码。
另外,也可以直接在原工程上右键新建一个lib工程,或者在工程中右键add Existing File..增加已经存在的lib工程进来(不要选择copy to folder)。这种形式是一个工程下面包含一个Lib工程。
2.在LibExample的目录中增加你需要加入的.h.m文件,然后查看在Build Phases->Compile Source中的文件,表示这些代码会被编译进lib中,你可以删掉你不希望被编译的,增加你想要编译进去的文件。
3在工程的target上双击,targets->Build Phases里面Target Dependencies里面增加lib工程的target,这样编译工程时也会编译lib工程生成lib.a文件。同时在Link Binary With Libraries中增加选择lib.a,表示对library库的引用。
4.使用Lib工程而非Target时,需要修改工程的Scheme->Build中增增加Lib工程的Target。这样才能编译工程的同时编译lib工程,生成.a。
5.引用lib头文件:在项目文件工程文件的target的build Setting->Header Search Paths中增加头文件路径(../文件名(lib工程文件名/ 例如../MyLibPrint/),这个路径适应于lib工程与项目工程在同一目录),选择成递归类型。
6.最后在工程中可以使用lib.a中的文件了,使用时引用一下lib工程的头文件,如果不报错说明头文件引用成功,然后就可以使用了。
Lib相关部分错误信息
1.undefine symbols for architecture i386 错误。
其实这个错误原因很简单,就是因为,我们用错了编译出来的libUITab.a lib,
在模拟器里面,我们需要的是基于i386构架编译的static lib,但是这个a文件,大家还记得前面说的arm6 arm7构架的么。这个a其实是在iphone这个arm构架上运行的代码。
那如何编译i386的库呢?运行之前选择Print>IOS Device,将这个iOS Device修改成iPhone5.0 Simulator。在进行编译,这样就可以编译出i386下面的库。
下面最多有四个文件夹分别命名为:Debug-iphoneos/Debug-iphonesimulator/Release-iphoneos/Release-iphonesimulator这四条目录每个目录下同样也有一个libPrint.a文件。Release-iphoneos里面的是基于arm6 arm7编译出来的库文件。Release-iphonesimulator文件夹下面的是基于i386编译出来的文件。
2.在编译RegexKitLite的时候,报错如下:
在项目的编译设置中找到Other Linker Flags,然后在后面字段空白处双击,添加“-licucore”就可以了,引用正则框架必须打开此开关。-licucore,注意不要打错,打错了会报错误:clang: error: no such file or directory: ‘-licucore'
对64bit的支持
Xcode5.1生成的项目,默认的便已选择是支持64bit编译的,可以查看工程的build Setting中的Architectures选项,包含了arm64的设置。
项目支持arm64要求项目中所引用的lib.a和.framework文件均支持arm64,否则就会报错。
ld: symbol(s) not found for architecture x86_64
当然,可以直接设置项目不支持x86_64。
- build Settings中在Architectures选项设置为armv7和armv7s
- Valid Architectures中删除arm64
但是能兼容肯定是更好的:
- lib或者framework工程创建是需要包含对arm64的支持。查看上述选项如果没有需要添加。
- 使用lipo -info查看是否包含x86-64,判断是否成功。
使用本文中的通用工程生成时,在模拟器编译时需要修改为如下,
xcodebuild -target ${TARGET_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 -arch x86_64 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"
增加ONLY_ACTIVE_ARCH=NO和-arch x86_64。然后编译即可生成符合要求的.a。
其他参考