A. 请教库冲突的导致的 plicate symbol 链接错误
场景:
解决TencentOpenAPI.framework与ZbarSDK中 _base64_encode 函数的冲突
后来在网络上搜寻,删除掉 Other Linker Flag 的 -all_load 就可以解决静态库冲突的问题,
但是这样做的话,会使一些外部的静态库,使用objc扩展函数(catagory)的方法失效。例如BaiMapApi
如果是有些库使用到了扩展函数(catagory)可以分别对这个库进行加载
使用:-force_load
-force_load BaiMapApi/libs/Release-iphoneos/libmapapi.a
(BaiMapApi是添加到当前目录下的)
或
-force_load $(BUILT_PRODUCTS_DIR)/libxxx.a
(这里是直接添加静态库项目源码的做法)
使用-force_load分别进行加载还是蛮方便的,如果有些函数加入了main函数使用all_load就相当麻烦了。
以上可以解决TencentOpenAPI.framework与ZbarSDK的冲突
如果两个静态库冲突的结构是相同的,可以考虑将两个静态库拆分出来进行合并。
查看文件的架构有哪些
$ lipo -info libzbar.a
Architectures in the fat file: libzbar.a are: armv7 (cputype (12) cpusubtype (11)) i386
将armv7解压出来
lipo libzbar.a -thin armv7 -output libzbar-armv7.a
新建立一个文件夹出来存放解压的(.o)文件
$ mkdir armv7
$ cd armv7
将静态库中的文件解压
$ ar -x ../libzbar-armv7.a
然后将另一个静态库根据以上的步骤做一遍,然后观察连个解压的静态库中,有那些是一样的就合并在一起,不过注意的是两个静态库冲突的(.o)文件必须一致,否则也会出现错误。
合并完后进行打包了
$ libtool -static -o ../libnew-armv7.a *.o
如果像在虚拟机也使用,进行相同的步骤后,将i386的架构合并再一起就可以了。
合并静态库
$ lipo -create -output lib.a libnew-armv76.a libi386.a
plicate symbol是一种常见的链接错误,不像编译错误那样可以直接定位到问题的所在。但是经过一段时间的总结,发现这种错误总是有一些规律可以找的。
例如,我们有如下的最简单的两个类代码:
// ClassA.h
#import <Foundation/Foundation.h>
@interface ClassA : NSObject
@end
// ClassA.m
#import "ClassA.h"
@implementation ClassA
@end
// ClassB.h
#import <Foundation/Foundation.h>
@interface ClassB : NSObject
@end
// ClassB.m
#import "ClassB.h”
@implementation ClassB
@end
编译后出现的错误信息如下:
plicate symbol _OBJC_METACLASS_$_ClassA in:
/Users/dajie/Library/Developer/Xcode/DerivedData/linkTest-cpjaaatiyqpvxcbzfzpklcbqrgqg/Build/Intermediates/linkTest.build/Debug-iphonesimulator/linkTest.build/Objects-normal/i386/ClassA.o
/Users/dajie/Library/Developer/Xcode/DerivedData/linkTest-cpjaaatiyqpvxcbzfzpklcbqrgqg/Build/Intermediates/linkTest.build/Debug-iphonesimulator/linkTest.build/Objects-normal/i386/ClassB.o
plicate
symbol _OBJC_CLASS_$_ClassA in:
/Users/dajie/Library/Developer/Xcode/DerivedData/linkTest-cpjaaatiyqpvxcbzfzpklcbqrgqg/Build/Intermediates/linkTest.build/Debug-iphonesimulator/linkTest.build/Objects-normal/i386/ClassA.o
/Users/dajie/Library/Developer/Xcode/DerivedData/linkTest-cpjaaatiyqpvxcbzfzpklcbqrgqg/Build/Intermediates/linkTest.build/Debug-iphonesimulator/linkTest.build/Objects-normal/i386/ClassB.old:
2
plicate symbols for architecture i386clang:
error: linker command failed with exit code 1 (use -v to see invocation)
从上面出现问题的地方,我们应该能推测出是ClassA这个类出了问题。如果这个类是我们自己写的,就容易办一些。
可以考虑以下原因:
1.引入头文件时,由于疏忽,误引入.m文件。这种一般仔细检查一下出现问题的类的源文件就能发现。
例:ClassB.m 文件修改成下面这样
#import "ClassB.h>"
#import "ClassA.m” // 这句话有问题
@implementation ClassB
@end
2.同一个类,实现两次,即有两个@implementation 。这种一般会有一个警告,也比较容易发现。
例:ClassB.m文件修改成下面这样:
// ClassB.m
#import "ClassB.h”
@implementation ClassB
@end
@implementation ClassA
@end
3.工程文件,同一个类文件被引入了两次,引起这种错误的原因大概有两种:一是多人协作开发时,导致project文件合并冲突;二是同名文件不在同一目录下,添加到工程时造成重复添加。 这种一般在文件视图,用名字过滤器检查一下就发现了。
4.
Targets的Build Phase设置项里,查看Complie
Sources这一项,看看出现问题的类是不是有重复的,用文件名过滤也比较容易发现。这种问题一般也是多人协作开发时,project文件冲突导致的。
发现这种问题,只要删除就可以了。不过在我解决问题过程中,删除其中一个文件时,重复文件会自动全部删除,所以还需要单独添加一下。
5.如果我们的工程中引用了第三方的库,而恰好第三方的库里面有一个ClassA,也会出现这种问题。如果不能修改第三方的库代码,只能修改我信自己的代码了。
B. ZBarSDK怎么可以快速读取和解码
ps:在调试过程中正好苹果发布了iOS7,于是也从Xcode 4升级了Xcode最新版,同样真机设备也从6.1.3升级到7.0,看看会不会有兼容问题,总体来说没有问题
下面介绍一下如何在iOS设备上使用二维码
1.扫描二维码(包括读取和解码)
扫描二维码的开源库有很多如 ZBar、ZXing等。博主先尝试了ZXing,竖屏的情况下ZXing没有问题,但是似乎横屏会出现错位的情况。于是博主放弃ZXing,使用ZBar。
这里要提一句,ZXing的开源做的比ZBar好,读取和解码速度也快,但是整合到自己的工程中是见痛苦的事情,不光是导入整个Demo工程,设置也相对繁琐,如果不是真的需要超高性能应用的开发者,博主还是建议使用ZBar。
总的流程如下:
①下载ZbarSDK,包含ZbarSDK头文件包、libzbar.a静态链接库文件以及资源文件三部分;
②在新工程中导入以下框架:AVFoundation.framework、CoreMedia.framework、CoreVideo.framework、QuartzCore.framework、libiconv.dylib(已存在则不需要重新加入);
③将ZbarSDK加入工程;
④检查libzbar.a链接库是否正常,如果没有自动链接,需要手动加入,如下图:
⑤在需要使用的页面.h文件中引用头文件#import "ZBarSDK.h"
⑥在需要使用的页面.h中继承 <ZBarReaderDelegate>协议
⑦在.m中实现协议的方法- (void) imagePickerController: (UIImagePickerController*) reader didFinishPickingMediaWithInfo: (NSDictionary*) info
[cpp] view plain
id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];
ZBarSymbol *symbol = nil;
for(symbol in results)
break;
[self dismissViewControllerAnimated:YES
completion:^{
}];
NSString *code = [NSString stringWithString:symbol.data];
⑧实现开始扫描的按钮功能
[cpp] view plain
ZBarReaderViewController *reader = [ZBarReaderViewController new];
reader.readerDelegate = self;
reader.supportedOrientationsMask = ZBarOrientationMaskAll;
ZBarImageScanner *scanner = reader.scanner;
[scanner setSymbology: ZBAR_I25
config: ZBAR_CFG_ENABLE
to: 0];
[self presentViewController:reader
animated:YES
completion:^{
}];
2.生成二维码
在生成二维码的库中QREncoder最为常见,但是由于中文字符的特殊性,生成中文的时候会出现一定的错误,所以博主改用libqrencode,是一个纯C编写的类库,支持面也更广泛。
①下载libqrencode源码;
②在新工程中导入以下框架:AVFoundation.framework、CoreMedia.framework、CoreVideo.framework、QuartzCore.framework、libiconv.dylib(已存在则不需要重新加入);
③将libqrencode源码加入工程;
④需要使用的页面.m文件中引用头文件#import "QRCodeGenerator.h";
⑤实现生成二维码图片的方法
[cpp] view plain
UIImage *image = [QRCodeGenerator qrImageForString:self.codeString imageSize:self.imageView_Code.bounds.size.width];
3.自定义扫描页
ZXing支持自己创建扫描页面,而由于ZBar使用的.a的链接库文件,所以需要修改他生成的扫描页,不能直接新建一个。
在上文中扫描二维码第⑧项,对reader进行一些修改,代码如下:
[cpp] view plain
for (UIView *temp in [reader.view subviews]) {
for (UIToolbar *toolbar in [temp subviews]) {
if ([toolbar isKindOfClass:[UIToolbar class]])
{
UIBarButtonItem *item1 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(dismissOverlayView:)];
UIBarButtonItem *item2 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem: target:nil action:nil];
UIBarButtonItem *item3 = [[UIBarButtonItem alloc]initWithTitle:@"相册" style:UIBarButtonItemStyleBordered target:self action:@selector(clickPhotoToolBarItem:)];
NSArray *array = [NSArray arrayWithObjects:item1,item2,item3,nil];
[toolbar setItems:array animated:YES];
}
}
}
上述代码中,取消按钮和相册按钮方法等需要自己定义。方法中详细代码如下:
[cpp] view plain
//取消按钮方法
- (void)dismissOverlayView:(id)sender{
[self dismissViewControllerAnimated:YES
completion:^{
}];
}
// 选取相册
-(void)clickPhotoToolBarItem:(id)sender
{
if ([UIImagePickerController isSourceTypeAvailable:])
{
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.allowsEditing = NO;
imagePicker.sourceType = ;
[self dismissViewControllerAnimated:NO completion:^{}];
[self presentViewController:imagePicker animated:YES completion:^{}];
}
}