ios静态库的使用

ios静态库的使用
  3906人阅读  评论(0)  收藏  举报

ios的静态库文件是*.a,如果需要使用它,我今天学的简单的方法,可通过,简单说说,如果有一个A手机项目,一个B的静态库项目,A想使用B.a,按以下的步骤:

1、在A项目里面拖进B项目。在B的product下面会看见红色的B.a,表示还没有编译通过(在工程设置里添加上你需要导出的.m文件)。

2、选择好需要编译的对象,B下的某模拟器或者是B下的真机上进行编译(模拟器上生成的静态库和真机上生成的不能混用)

3、在A里面新建一个文件夹(new group),里面拖进你需要B里面导入的头文件。

4、在A的framework里加入编译好的.a静态库,编译通过的就不会是红色。

5、在需要使用的地方#import 所需的头文件。ok了!


下面二篇,是别人写的生成和使用静态库,里面还有资源的绑定方法,可借鉴!

一、IOS开发----生成静态库(.a)

由于iPhone控件的极度匮乏和自定义组件在重用上的限制,在过去的项目中我们积累了大量的“纯代码”组件——因为IB本身的限制,我们无法把这些组件封装为IB组件库(本来我们想通过分发xib文件的方式重用这些组件,但最终发现这根本不可能,苹果的Plug-in编程不支持iPhone)。

最终我们想到了静态库。虽然这仍然还是一种比较原始的复用方式,但起码我们可以隐藏组件的源代码。

下面, 我们使用iPhone静态库把自定义组件CheckButton 进行进一步的封装。(组件的实现参考前一篇博文《自定义控件复选框和单选框的实现》)

一、实现静态库

新建工程, 选择 Library 下的 “ Cocoa Touch Static Library ” 。给工程命名,例如:yhyLibrary。                 

复制CheckButton组件的4个源文件:CheckButton.h、CheckButton.m、RadioGroup.h、RadioGroup.m到Classes目录下,同时把CheckButton的4个资源文件:check.png、uncheck.png、radio.png、unradio.png,复制到工程文件夹。

按下 ⌘ +b编译,在Products目录下即产生一个 .a文件。

二、 新建资源束

静态库中并不能包含资源文件,虽然我们已经把4个资源文件(.png文件)拷贝到静态库工程中,但实际上这些.png是不会添加到target的,也就是说编译结果中并不包含这些资源,因此如果此时调用静态库,所有的资源(字符串、图片)都是缺失的。

我们可以把资源建立成单独的束(Bundle)。

新建工程“ Mac OS X  -> Framework & Library -> Bundle ”,命名为:yhyLibraryBundle。

然后把上面4个.png文件拷进Resouces中去。编译,生成yhyLibraryBundle.bundle文件。

返回静态库工程,新建一个类:Utils 。

编辑Utils.h:

#define MYBUNDLE_NAME @ "yhyLibraryBundle.bundle"

#define MYBUNDLE_PATH [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: MYBUNDLE_NAME]

#define MYBUNDLE [NSBundle bundleWithPath: MYBUNDLE_PATH]

NSString * getMyBundlePath( NSString * filename);

编辑Utils.m:

#import "Utils.h"

NSString* getMyBundlePath( NSString * filename)

{

NSBundle * libBundle = MYBUNDLE ;

if ( libBundle && filename ){

NSString * s=[[libBundle resourcePath stringByAppendingPathComponent : filename];

NSLog ( @"%@" ,s);

return s;

}

return nil ;

}

函数getMyBundlePath可以取得束yhyLibraryBundle中具体资源的绝对文件路径,如:

/Users/kmyhy/Library/Application Support/iPhone Simulator/4.2/Applications/8213652F-A47E-456A-A7BB-4CD40892B66D/yhyLibTest.app/yhyLibraryBundle.bundle/Contents/Resources/radio.png

同时,修改CheckButton.m中的代码,导入Utils.h头文件,把其中获取图片的代码由imageNamed修改为imageWithContentsOfFile,如:

[ icon setImage :[ UIImage imageWithContentsOfFile : getMyBundlePath ( checkname )]];

即通过绝对路径读取图片资源。

除了这种方法,我们还可以有一个简单办法,就是把4个资源文件直接拷贝到你调用静态库的应用工程中(不需要修改静态库代码)。

 

三、静态库调用

1、添加静态库

新建Window-based Application工程,给工程命名,如yhyLibraryTest。

右键点 Frameworks->Add->Existing Files.. ,把静态库工程的yhyLibrary.xcodeproj文件 添加到当前工程(不要选择 Copy items ) 。

 

选中添加进来的yhyLibrary.xcodeproj文件,勾选“include to target”选项,如下图,打上最后一个小勾:

 

2、添加Direct Dependencies(即引用工程)

类似于Visual Studio中的引用工程,目的是便于在本工程中直接编辑所引用的静态库工程,以便对静态库进行修改。

在“ Targets ”目录下选择“ FirstLibraryTest ”,点击“info”按钮,调出目标的属性窗口,切换到“General”栏,点击“ Direct Dependencies ”下方的“ + ”按钮,将工程静态库libyhyLibrary添加到Direct Dependencies中,结果如下图:

 

 

3、添加头文件搜索路径

打开工程的info窗口,在Build栏中找到Header Search Paths,添加字符串“../yhyLibrary”。

4、 引用资源束

在target的Copy Bundle Resources上右键,选择“Add->Existing File…”,把前面生成的yhyLibraryBundle.bundle束添加到工程。

5、调用静态库中的类

编辑 application:( UIApplication *)application didFinishLaunchingWithOptions: 方法中的代码:

// 单选按钮组

RadioGroup rg =[[ RadioGroup alloc ] init ];

//  1 个单选按钮

CheckButton * cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 60 260 32 )];

// 把单选按钮加入按钮组

[ rg add :cb];

cb. label . text = @"★" ;

cb. value =[[ NSNumber alloc ] initWithInt : 1 ];

// 把按钮设置为单选按钮样式

cb. style = CheckButtonStyleRadio ;

// 加入视图

[ self . window addSubview :cb];

[cb release ]; //add 后,会自动持有,可以释放

//  2 个单选按钮

cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 100 260 32 )];

[ rg add :cb];

cb. label . text = @"★★" ;

cb. value =[[ NSNumber alloc ] initWithInt : 2 ];

cb. style = CheckButtonStyleRadio ;

[ self . window addSubview :cb];

[cb release ];

//  3 个单选按钮

cb=[[ CheckButton alloc ] initWithFrame : CGRectMake ( 20 140 260 32 )];

[ rg add :cb];

cb. label . text = @"★★★" ;

cb. value =[[ NSNumber alloc ] initWithInt : 3 ];

cb. style = CheckButtonStyleRadio ;

[ self . window addSubview :cb];

[cb release ];

运行结果如下:

 

6、分发静态库

将生成的.a文件和.bundle文件打包分发给其他人。


二、使用静态链接库(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


UILabelEX 是一个类别扩展,因为之前有人说,类别扩展不能放到静态库中,所以亲自试验一下:

#import <UIKit/UIKit.h>

@interface UILabel (TestColor)

- (void) testMethodColor;

@end

#import "UILabelEX.h"

@implementation UILabel (TestColor)

- (void) testMethodColor {

    NSLog(@"testMethodColor");

}

@end


并且确保MyLib 的 Compile Sources 中包含我们刚刚创建类的 .m文件,因为这里添加了哪些.m文件,就相当于MyLib静态库对外提供了什么接口,如果没有加入,就要手动点击+来加入了:



然后关闭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和新工程里需要引用的头文件都添加进新工程,这样就可以了。)


我这里以使用模拟器为例,来让DemoTwo编译生成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


代码里明明 UILabel可以找到testMethodColor,但实际上没找到这个方法,这个地方还有个细节,答案在这里:http://developer.apple.com/library/mac/#qa/qa1490/_index.html

DemoOne的 build setting中,搜索 Other Linker Flags,找到设置后,在其中添加一个参数 -ObjC,再编译及运行,貌似一切都OK了。




其它:

1、如果你没有完全按步骤来弄,可能会出现如下错误,一般用模拟器做的项目可能会遇到这个问题:

  1. Undefined symbols for architecture i386:  
  2.   "_OBJC_CLASS_$_RequestServer", referenced from:  
  3.       objc-class-ref in ListViewController.o  
  4.       objc-class-ref in SettingsViewController.o  
  5. ld: symbol(s) not found for architecture i386  
  6. clang: error: linker command failed with exit code 1 (use -v to see invocation) 
不要担心,去DemoTwo的lib中,把对外接口的.m文件添加进去:



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删除
方法三:如果有libxxx.a的源代码再编译一个libxxx.a的armv7s版本,然后合并到之前的libxxx.a中


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 文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值