相关链接: IOS7开发~Xcode5制作framework
使用静态链接库(Xcode4.6.2)
一、理论部分
在实际的编程过程中,通常会把一些公用函数制成函数库,供其它程序使用,一则提搞了代码的复用;二则提搞了核心技术的保密程度。所以在实际的项目开发中,经常会使用到函数库,函数库分为静态库和动态库两种。和多数人所熟悉的动态语言和静态语言一样,这里的所谓静态和动态是相对编译期和运行期的:静态库在程序编译时会被链接到目标代码中,程序运行时将不再需要改静态库;而动态库在程序编译时并不会被链接到目标代码中,只是在程序运行时才被载入,因为在程序运行期间还需要动态库的存在。
静态链接库适用于:
1.你想将一部分以后都不会修改的代码打包,供其他项目使用
2.你想将一部分代码封装起来给别人用,又不愿别人看到你的实现方法
二、实践部分
如何制作静态链接库(以下简称lib):
1。如果是新工程。创建工程的时候选Framework&Library -> cocoa touch static library,就直接创建了一个静态链接库工程,默认会有两个跟工程名相同的.h和.m,继续添加文件,m都会自动加入到Build Phases->Compile Source中,表示这些代码会被编译进lib中,你可以删掉你不希望被编译的。
2. 如果是项目工程,想抽取一个lib出来,就add target,也是选Framework&Library -> cocoa touch static library。在xcode navigator里会多一个文件夹,和你新创建的target同名。同样,你可以在Build Phases->Compile Source里,添加你希望加入到lib中的文件。
下面:新建两个单视图模版项目DemoOne,DemoTwo,其中,我想把DemoTwo作为静态库,然后在DemoOne中使用:
a、打开DemoTwo
鼠标选择:
然后 点击Add Target,选择 Framework & Library -> Cocoa Touch Static Library -> 新建一个名字叫MyLib的库:
其中,MyLib这个target,就是我们想对外提供的库,这个库的对外提供的接口,是我们自己可以任意控制的,当然可以加多个target,每个target静态库可以提供不同接口,我这里只做一个静态库MyLib。让MyLib这个target 和 DemoTwo 建立时的默认target DemoTwo功能类似,所以还要给MyLib 添加 Frameworks:
然后开始编写MyLib这个库想对外提供哪些功能了,在DemoTwo项目中建立一个group,命名为LibMethod,并在其中新建三个类Func1,Funk2,UILabelEX,其中实现的代码都很简单,打印log而已。
Func1 和 Func2 类似,拿Func1举例:
@interface Func1 : NSObject
- (void) func1Log;
@end
#import "Func1.h"
@implementation Func1
- (void) func1Log {
NSLog(@"Func1 log");
}
@end
#import <UIKit/UIKit.h>
@interface UILabel (TestColor)
- (void) testMethodColor;
@end
#import "UILabelEX.h"
@implementation UILabel (TestColor)
- (void) testMethodColor {
NSLog(@"testMethodColor");
}
@end
然后关闭DemoTwo项目,打开DemoOne项目,打开DemoTwo项目文件夹,把其中的 DemoTwo.xcodeproj 拖拽到DemoOne中:
然后给DemoOne添加库,选择我们在DemoTwo中创建的MyLib:
如果libMyLib.a为红色,表明DemoTwo,没有编译生成libMyLib.a,不要慌,这个是小事情:
理论(在编译之前,在target的scheme中选build configuration选择模拟器,然后编译。
注意,你用device模式编译出的lib只能真机运行,模拟器模式编译出的lib只能用于模拟器调试。然后找到编译出lib,复制到需要它的工程里。
如果你希望一个lib既可以在模拟器上运行,又可以在真机上运行,那就各编译一次吧,把两个lib都找到,用命令把两个lib合并成一个,命令是:lipo -create sim.a dev.a -ouput libXX.a 合并产生的libXX.a就可以两用了。
把lib和新工程里需要引用的头文件都添加进新工程,这样就可以了。)
这个有个细节问题,就是你生成的lib想用在真机,还是模拟器?很简单, 首先选择 Mylib,然后在点击其响应下的 Edit Scheme,最上边可以选择是模拟器还是真机,然后build一下:
此时发现DemoOne中的,依赖库正常了吧:
然后就开始让DemoOne来使用DemoTwo提供的借口吧:
单首先要在DemoOne中引入下DemoTwo中的接口,DemoOne新建group ,名字 lib,然后把DemoTwo中的接口.h文件,以引用的形式拖拽到lib文件夹中(如果不以引用形式,当DemoTwo中接口代码改变时,DemoOne中的接口文件不会随着改变):
到此时,工作基本完成了,然后在DemoOne的 ViewController中实现如下代码:
#import "ViewController.h"
#import "Func1.h"
#import "Func2.h"
@implementation ViewController
- (void)viewDidLoad
{
[superviewDidLoad];
Func1 *obj1 = [[Func1alloc]init];
[obj1 func1Log];
Func2 *obj2 = [[Func2alloc]init];
[obj2 func2Log];
}
@end
编译及运行,不出意外,应该有log打印了,说明我们基本成功了。
不过是不是少了点是吗?对 #import "UILabelEX.h" 这个类还没使用呢,这个类是对UILabel的类型扩展:
#import <UIKit/UIKit.h>
@interface UILabel (TestColor)
- (void) testMethodColor;
@end
#import "UILabelEX.h"
@implementation UILabel (TestColor)
- (void) testMethodColor {
NSLog(@"testMethodColor");
}
@end
那使用一下吧:
UILabel *obj3 = [[UILabel alloc]init];
[obj3 testMethodColor];
然后编译并运行,发现项目crash了,原因:-[UILabel testMethodColor]: unrecognized selector sent to instance 0x7574190
DemoOne的 build setting中,搜索 Other Linker Flags,找到设置后,在其中添加一个参数 -ObjC,再编译及运行,貌似一切都OK了。
其它:
1、如果你没有完全按步骤来弄,可能会出现如下错误,一般用模拟器做的项目可能会遇到这个问题:
2、如果做真机使用的 lib,可能会遇到arm7相关的错误,我暂时没遇到,引用别人的解决办法如下(我没亲自试验过):
Xcode4.5.2、iOS6应用中静态库不支持armv7s的解决方法
错误详细信息:
ld: file is universal (3 slices) but does not contain a(n) armv7s slice: /zhangyg/XXX/XXX/libs/libxxx.a for architecture armv7s
clang: error: linker command failed with exit code 1 (use -v to see invocation)
解决方法如下:
方法一:把上图Build Active Architecture Only的值设置为Yes
方法二:把上图Valid Architectures中的armv7s删除
3、模拟器运行正常,但真机会crash,打印如下错误:
dyld: lazy symbol binding failed: Symbol not found: _objc_setProperty_atomic_copy
Referenced from: /var/mobile/Applications/DFCB17A5-52AC-41CD-9ECB-94415C7D36F3/kalagame-demo.app/kalagame-demo
Expected in: /usr/lib/libobjc.A.dylib
dyld: Symbol not found: _objc_setProperty_atomic_copy
Referenced from: /var/mobile/Applications/DFCB17A5-52AC-41CD-9ECB-94415C7D36F3/kalagame-demo.app/kalagame-demo
Expected in: /usr/lib/libobjc.A.dylib
解决办法:
这个错误就是说App可执行文件里引用了objc_setProperty_nonatomic或objc_setProperty_atomic这些函数。但是代码里显然没有直接调用这2个函数,应该是系统在编译时生成的。经过Debug调试发现总是在设置一个对象的属性时出现这个错误。而这个对象的类定义在静态库里面,所以我看了看静态库。
经过排查,发现导致这一问题的原因是这个静态库的Deployment Target设置成了6.0。因为objc_setProperty_nonatomic和objc_setProperty_atomic是iOS6中新增的函数,所以如果静态库的Deployment Target设置成iOS6,那么编译后就会使用objc_setProperty_nonatomic和objc_setProperty_atomic这些新的API。由于iOS5中没有这些API,运行后将会崩溃。
结论
静态库在编译时,Deployment Target一定要低于和等于工程的Deployment Target。否则容易出现低版本iOS运行不兼容的情况。
4、突然项目要更改静态库项目的工程名称,于是右件静态库项目可执行文件(蓝颜色的那个),弹出菜单中选 Show File Inspector,然后XIB属性修改入口弹出,可以修改项目名称,但修改完项目名称之后,发现静态库提供的几个接口失效了。
报错:Undefined symbols for architecture armv7:
解决办法:在引用静态库的项目设置中,重新添加静态库文件,就是那个 XXX.a 文件。
虽然结果出来了,但难免会有考虑不周之处,还请麻烦指出,不胜感激。
完整项目链接:http://pan.baidu.com/share/link?shareid=4016251631&uk=3674861929
结束了~