iOS中创建使用链接库

来源:互联网 发布:软件成本估算公式 编辑:程序博客网 时间:2024/06/13 03:14

一、动态链接库与静态链接库的区别

  库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。 

1. 静态函数库

    这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个 函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。

2. 动态函数库

    这类库的名字一般是libxxx.so;相对于静态函数库,动态函数库在编译的时候 并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。

二、使用库的优势

使用静态库的好处

1,模块化,分工合作

2,避免少量改动经常导致大量的重复编译连接

3,也可以重用,注意不是共享使用

 

动态库使用有如下好处:

1使用动态库,可以将最终可执行文件体积缩小

2使用动态库,多个应用程序共享内存中得同一份库文件,节省资源

3使用动态库,可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。

三、iPhone对于链接库的支持

iPhone官方只支持静态库联编,不支持动态链接库。而对于静态库有两种方式:一种是生成.a文件;一种是生成静态framework。所谓的静态framework,实际上也是一种静态库,其相对于.a文件来说,区别在于其不仅包含了库文件,还包含了头文件和资源文件。而在使用.a文件时,还要另外添加.h文件。

iPhone不支持动态链接库的原因可能有:

①共享给谁?(因为在现在的iPhone,iPodTouch,iPad上面程序都是单进程的,也就是某一时刻只有一个进程在运行,你使用的时候只有你一个应用程序存在,其他的应该被挂起了,即便是可以同时多个进程运行,别人能使用你的共享库里的东西吗?你这个是给你自己的程序定制的。)

②目前苹果的AppStore不支持模块更新,无法更新某个单独文件(除非自己写一个更新机制:有自己的服务端放置最新动态库文件),也就是说,就算你通过其他方式产生了动态链接库,也无法达到更新动态库文件从而更新应用程序的目的。

四、Xcode对于创建库的支持

Xcode不支持自己创建动态链接库。而framework也分为静态框架和动态框架,大部分框架都是动态链接库的形式。因为只有苹果才能在iOS设备上安装动态库,所以我们也无法创建动态框架。

而且苹果也从Xcode中移除了创建静态iOS框架的功能!!!

通常来说,Xcode只支持创建静态库文件.a。但是实际上Appstore是允许代码封装在静态链接库中的。所以,出现了所谓的伪框架真框架

五、框架的类别和使用

前面说了,框架分为静态框架和动态框架,对于静态框架,在生成过程中又有了的区别。

1框架

框架是破解的“reloacatable object file”(可重定位格式的目标文件, 保存着代码和数据,适合于和其他的目标文件连接到一起,用来创建一个可执行目标文件或者是一个可共享目标文件),它可以让Xcode编译出类似框架的东西——其本质是一个bundle

伪框架模板把整个过程分为几个步骤,用某些脚本去产生一个真正的静态框架(基于静态库而不是reloacatable object file)。而且,框架项目还是把它定义为wrapper.cfbundle类型,一种Xcode中的二等公民

因此它跟静态框架一样可以正常工作,但当存在依赖关系时就有麻烦了。

依赖问题

如果不使用依赖,只是创建普通的项目是没有任何问题的。但是如果使用了项目依赖(比如在workspace中),Xcode就悲剧了。当你点击“Link Binary With Libraries”下方的’+’按钮时,伪框架无法显示在列表中。你可以从你的框架项目的Products下面将它手动拖入,但当你编辑你的主项目时,会出现警告:

warning: skipping file '/somewhere/MyFramework.framework' (unexpectedfile type 'wrapper.cfbundle' in Frameworks & Libraries build phase)

并伴随框架中的链接错误。

幸运的是,有个办法来解决它。你可以在”Other Linker Flags”中用”-framwork”开关手动告诉linker去使用你的框架进行链接:

-framework MyFramework

警告仍然存在,但起码能正确链接了。

2框架

框架各个方面都符合的标准。它是真正的静态框架,正如使用苹果在从Xcode中去除的那个功能所创建的一样。

为了能创建真正的静态框架项目,你必需在Xcode中安装一个xcspec文件。

如果你发布一个框架项目(而不是编译),希望去编译这个框架的人必需也安装xcspec文件(使用本次发布的安装脚本),以便Xcode能理解目标类型。

注意:如果你正在发布完全编译的框架,而不是框架项目,最终用户并不需要安装任何东西

3、不同框架类型的使用时机

简单来说,你可以这样决定用哪一种框架:

·         如果你不想修改Xcode,那么请使用“伪”框架版本

·         如果你只是想共享二进制(不是项目),两种都可以

·         如果你想把框架共享给不想修改Xcode的开发者,使用“伪”框架版本

·         如果你想把框架共享给修改过Xcode的开发者,使用“真”框架版本

·         如果你想把框架项目作为另一个项目的依赖(通过workspace或者子项目的方式),请使用“真”框架(或者“伪”框架,使用-framework——见后)

·         如果你想在你的框架项目中加入其他静态库/框架,并把它们也链接到最终结果以便不需要单独添加到用户项目中,使用“伪”框架

六、IOS静态框架的创建、编译和使用

1、安装框架模板

要想在xcode中创建静态框架,须得先安装相应的模板iOS Universal Framework Mk

iOS Universal Framework Mk 8的下载地址:https://github.com/kstenerud/iOS-Universal-Framework

解压下载文件,打开Terminal,进入到刚下载文件,Fake Framework下面。输入命令:sh install.sh。同理安装Real Framework下的文件。安装完毕如下图:

我的图片一

分别运行Real Framework目录或Fake Framework目录下的install.sh脚本进行安装(或者两个你都运行)。

重启Xcode,你将在新项目向导的Framework&Library下看到StaticiOS Framework(或者Fake Static iOS Framework)。

若要卸载这两个模板,请运行unistall.sh脚本并重启Xcode

2、  创建一个IOS框架项目

①创建新项目。

②项目类型选择Framework&Library下的Static iOS Framework(或者Fake Static iOS Framework)。

③ 选择“包含单元测试”(可选的)。

④在target中加入类、资源等。

⑤凡是其他项目要使用的头文件,必需声明为public。进入target的Build Phases页,展开Copy Headers项,把需要public的头文件从Project或Private部分拖拽到Public部分。

3、  编译IOS框架、

①    选择指定target的scheme。

②修改scheme的Run配置(可选)。Run配置默认使用Debug,但在准备部署的时候你可能想使用Release。

③编译框架(无论目标为iOS device和Simulator都会编译出相同的二进制,因此选谁都无所谓了)。

④从Products下选中你的framework,“show in Finder”。

build目录下有两个文件夹:(yourframework).framework and (your framework).embeddedframework.

如果你的框架只有代码,没有资源(比如图片、脚本、xibcoredatamomd文件等),你可以把(yourframework).framework 分发给你的用户就行了。如果还包含有资源,你必需分发(your framework).embeddedframework给你的用户。

为什么需要embedded framework?因为Xcode不会查找静态框架中的资源,如果你分发(your framework).framework, 则框架中的所有资源都不会显示,也不可用。

一个embedded framework只是一个framework之外的附加的包,包括了这个框架的所有资源的符号链接。这样做的目的是让Xcode能够找到这些资源

4、  使用静态框架

iOS框架和常规的Mac OS动态框架差不多,只是它是静态链接的而已。

在你的项目中使用一个框架,只需把它拖仅你的项目中。在包含头文件时,记住使用尖括号而不是双引号括住框架名称。例如,对于框架MyFramework

#import <MyFramework/MyClass.h>

七、静态库.a文件的编写

IOS产生.a的静态库,比起.framework相对简单了好些。

 下面介绍一下具体生成步骤:

1、新建一个framework&library库。IOS 下的cocoa touch static library。然后输入product name libsql

 

2、把libsql.hlibsql.m删除。导入ocsqlite.hocsqlite.c(文件见http://blog.csdn.net/fengsh998/article/details/8278978

3、修改scheme,设为release版本。

 

OK,选译ios device编译运行。成功后将在目录的build/products/release-iphoneos/下产生一个liblibsql.a文件。

注,这里产生的是真机使用的.a文件。

选译iphonesimulator 进行编译一次,同样会在build/products/release-iphonesimulator/下产生一个liblibsql.a文件。

这里是虚拟机使用的.a文件。

 

下面来看一下这两个文件有什么不同之处,使用lipo -info命令。

打开终端。

进入到相应的目录。

真机的:liblibsql.a文件信息。

input file liblibsql.a is not a fat file

Non-fat file: liblibsql.a is architecture: armv7

如图:

模拟器的:liblibsql.a文件信息。

input file liblibsql.a is not a fat file

Non-fat file:  liblibsql.a  is architecture: i386

如图:

如果使用真机和模拟器通用,则需要将这两个文件合并,使用命令lipo -create  xxxx/liblibsql.a   xxxxx/liblibsql.a  -output  libsql.a

同样可以使用lipo -info 来查看这个合并的libsql.a

可以看到architectures in the fat file: libsql.a are: i386  armv7

如图:

八、iOS中创建,使用动态库(dylib)

由于苹果不支持自己创建动态库,所以这里需要替换两个文件

iOS Device 需要替换的文件

替换路径:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Specifications/

iOS 模拟器需要替换的文件

替换路径:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications

具体可参照:http://blog.iosplace.com/?p=33    或者  http://www.cocoachina.com/bbs/simple/?t129814.html 

替换完成后重启Xcode

创建动态库

 

1:打开Xcode,新建项目,选择OS X --> Cocoa Library -->输入动态库的相关信息

 

2:这样,动态库已经创建好,但是由于此项目是基于Mac OS X创建的,所以这里要将project的相关设置作修改

 

     1:Base  设置成 SDK iOS6.0

 

     2:Architectures  设置成 standrand armv7 armv7s

 

     3:Installation Directory  设置成 @executable_path/

 

     4:Mach-O Type  设置成 Dynamic Library

 

     5:Executable Prefix 设置成 

 

     6:打开项目的project.pbxproj(文本编辑器打开)文件,在编辑器中将producttype 的值修改为 com.apple.product-type.library.dynamic

 

     7:选择合适的证书文件

 

3:debug改为no

4:在动态库的相关类中添加一些测试方法

5:删除多余的framework文件。值添加Foundation.framework文件

 

Xcode--->Preferences--->Locations--->Advanced---->

 

这样基本就可以编译dylib了,但是这里还有一个需要注意的地方。

编译证书的选择

 

编译分为设备编译(iOS Device模拟器编译(iPad/iPhone Simulator)

在选择设备编译的时候,一定要选择某个有效的开发者证书。否则编译会出错。

相反,选择模拟器编译的时候,不需要选择证书(如果选了证书,也会报错)。

 

如果一切正常,那么在编译后的文件会出现在项目文件夹的Build/Products/.…文件夹中

 

参考文档:1http://blog.csdn.net/stackhero/article/details/9032999关于静态库、动态库的区别汇总

                      2http://blog.csdn.net/stackhero/article/details/9032891 iOS中创建,使用动态库(dylib)

                3、http://blog.csdn.net/fengsh998/article/details/8291965 IOS 4.2 编写通用的静态库.a文件


基本知识

在实际的编程过程中,通常会把一些公用函数制成函数库,供其它程序使用,一则提搞了代码的复用;二则提搞了核心技术的保密程度。

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'

其他参考

Library官方文档:https://developer.apple.com/library/ios/technotes/iOSStaticLibraries/Introduction.html#//apple_ref/doc/uid/TP40012554-CH1-SW1

XCode的各种参数配置参考:http://www.cnblogs.com/xiaodao/archive/2012/03/28/2422091.html

0 0