iOS上的Google协议缓冲区

19
4个回答

19

好的。看起来在这种情况下,元语法库(或其他第三方库)是不必要的。您可以直接将Google源代码添加到您的项目中。我在谷歌讨论组中找到了Nicola Ferruzzi的以下答案...

原始答案在这里...

http://groups.google.com/group/protobuf/browse_thread/thread/ca4218d7db144252

这个答案的内容如下,包含图片以便做成永久记录...

编辑

今晚再次尝试时,除了下面概述的步骤,我还需要几个步骤(这适用于protobuf 2.5.0)。

  • 您需要链接到libz.dylib。您可以在“Build Phases”>“Link Binary With Libraries”中设置此项。
  • 要轻松删除所有单元测试相关内容,请使用以下命令从google目录中的shell中执行:find . -name "*unittest*" -exec rm -rf {} \;
  • 还要删除名为testing的文件夹
  • stringprintf.cc中注释掉#include <google/protobuf/testing/googletest.h>
  • 现在仔细按照下面的说明操作,一切都应该正常工作。

I use latest version in my app .. you don't really need objc direct support if you are familiar with C++, there is only one point where you have to pass from std::string to NSData and viceversa. And its pretty simple.

To compile and test the easiest way Ive found is to just import the whole google directory in my own project :) (the second time you can make your own framework but for testing this procedure just works)

  • download latest version
  • autogen configure and make like you were just building for macosx (you need command line tools) . This way you end up with protoc
    binary and the library for macosx (which you don't need)
  • open your Xcode iOS project
  • add "new file" to your project and select google directory
  • add the directory of google headers to your additional include directories
  • add config.h from the protobuffer src directory to your app
  • from the google group remove everything that contains unitest :)
  • from the google group remove compiler and java stuff;

You should be able to compile without any linking error. To give you an idea this is what I directly compile

enter image description here

Then you can use protoc to generate c++ source files for your protocol. To use them with objc you have to rename your source to file "mm" then you can do something like

TO SERIALIZE TO NSDATA

let's say your message is called Packet

- (NSData *)getDataForPacket:(Packet *)packet { 
    std::string ps = packet->SerializeAsString(); 
    return [NSData dataWithBytes:ps.c_str() length:ps.size()]; 

TO READ FROM NSDATA

- (Packet *)getPacketFromNSData:(NSData *)data { 
  char raw[[data length]]; 
  Packet *p = new Packet; 
  [data getBytes:raw length:[data length]]; 
  p->ParseFromArray(raw, [data length]); 
  return p; 

}

当你说“将Google头文件目录添加到附加包含目录中”时,这是什么意思?是将其添加到头文件搜索路径中吗? - Tavis Bones
2
如果有人按照这个步骤出现问题,显示找不到<tr1/unordered_map>,则需要在config.h文件中删除'tr1'。此外,如果您无法编译message.cc,请在message.cc文件中添加include <iostream> - Tony
2
按照这里提供的一般指示,我已经构建了一个Cocoapod,可以直接从Google tar.gz文件安装Google Protobuf v2.5.0。它适用于Xcode 5和目标iOS 7。它还包括Mac OS的protoc编译器版本,因此可以将Protobuf支持添加到项目中,而无需进行其他任何操作。podspec在主要的Cocoapods Specs存储库中。只需“pod search protobuf”即可找到它。 - Bennett Smith
在按照您的描述并使用Xcode 6 beta6后,我收到了以下错误:未找到“unordered_map”文件。我错过了什么? - Teddy
1
好的,我找到了。C++语言方言必须设置为C++11,标准库为libc ++。 - Teddy
显示剩余2条评论

14

您可以通过在Podfile中添加以下行来使用Cocoapods将Google Protocol Buffers的支持添加到Xcode 5项目中。

pod 'GoogleProtobuf', '~> 2.5.0'

这将把Protobuf的C++版本代码放入您项目的Pod中。它还会将protoc编译器添加到Pods/GoogleProtobuf/bin/protoc文件夹中。

您可以在项目中创建自定义构建规则,自动将.proto文件转换为.ph.{h,cc}文件。以下是我如何实现的:

设置一个构建规则来“处理名称匹配为:*.proto的源文件,使用自定义脚本”。该脚本应包括以下内容:

cd ${INPUT_FILE_DIR}
${SRCROOT}/Pods/GoogleProtobuf/bin/protoc --proto_path=${INPUT_FILE_DIR} ${INPUT_FILE_PATH} --cpp_out=${INPUT_FILE_DIR}/cpp

将输出文件设置为包括以下内容:

$(INPUT_FILE_DIR)/cpp/$(INPUT_FILE_BASE).pb.h
$(INPUT_FILE_DIR)/cpp/$(INPUT_FILE_BASE).pb.cc

您包含在项目中的任何.proto文件现在都将自动转换为C++,然后作为构建的一部分进行编译。


请参考 https://dev59.com/UnjZa4cB1Zd3GeqPaB1o#19582682,了解另一种可能的本地构建解决方案。 - Bennett Smith

12

编辑:我之前回答了这个问题,但被版主删除了。所以我包含了一些教程中的代码。

一个几乎与上面发布的答案相同的教程 - 在iOS和Mac上使用Google协议缓冲区进行Objective-C编程

按照learnvst的答案给出的步骤,并参考评论中的陷阱。我遵循了完全相同的步骤,除了

将谷歌头文件目录添加到您的其他包含目录中 我在头搜索路径中添加了src/目录,而不是google目录。

此外,当我执行#import xyz.pb.h时,项目无法构建。当我将我的.m文件重命名为.mm时,我能够构建。这一点在教程中非常微妙:P。

基本上,导入任何.pb.h文件的.m文件,应该重命名为扩展名.mm

Here's some content from the tutorial -
PROTO文件
package kotancode;

enum ZombieType {
    SLOW = 0;
    FAST = 1;
}

message ZombieSighting {
    required string name = 1;
    required double longitude = 2;
    required double latitude = 3;
    optional string description = 4;
    required ZombieType zombieType = 5 [default = SLOW];
}

ZombieSightingMessage.h

// -- ZombieSightingMessage.h - note my C++ object is not in the public interface.
#import <Foundation/Foundation.h>

@interface ZombieSightingMessage : NSObject
- (void)doSomething;
@end

ZombieSightingMessage.mm

// -- ZombieSightingMessage.mm
#import <UIKit/UIKit.h>
#import "ZombieSightingMessage.h"
#import "zombie.pb.h"

@implementation ZombieSightingMessage

- (void)doSomething {
    // Doing random stuff with a UIView here to show the mixing
    // of C++ and Objective-C/Cocoa syntax in the same file...
    UIView *uiView = [[UIView alloc] init];
    [uiView setCenter:CGPointMake(20, 10)];

    // instantiate my protobuf-generated C++ class.
    kotancode::ZombieSighting *zombieSighting = new kotancode::ZombieSighting();
    zombieSighting->set_name("Kevin");
    zombieSighting->set_description("This is a zombie");
    zombieSighting->set_latitude(41.007);
    zombieSighting->set_longitude(21.007);
    zombieSighting->set_zombietype(kotancode::ZombieType::FAST);

    // Some small tomfoolery required to go from C++ std::string to NSString.
    std::string x = zombieSighting->DebugString();
    NSString *output = [NSString stringWithCString:x.c_str() encoding:[NSString defaultCStringEncoding]];
    NSLog(@"zombie: %@", output);

    // Instantiate another zombie from the previous zombie's raw bytes.
    NSData *rawZombie = [self getDataForZombie:zombieSighting];
    kotancode::ZombieSighting *otherZombie = [self getZombieFromData:rawZombie];

    // Dump the second zombie so we can see they match identically...
    NSString *newOutput = [NSString stringWithCString:otherZombie->DebugString().c_str() encoding:[NSString defaultCStringEncoding]];
    NSLog(@"other zombie: %@", newOutput);

    // Grimace all you want, but this is C++ and we need to clean up after ourselves.
    free(zombieSighting);
    free(otherZombie);

}

// Serialize to NSData. Note this is convenient because
// we can write NSData to things like sockets...
- (NSData *)getDataForZombie:(kotancode::ZombieSighting *)zombie {
    std::string ps = zombie->SerializeAsString();
    return [NSData dataWithBytes:ps.c_str() length:ps.size()];
}

// De-serialize a zombie from an NSData object.
- (kotancode::ZombieSighting *)getZombieFromData:(NSData *)data {
    int len = [data length];
    char raw[len];
    kotancode::ZombieSighting *zombie = new kotancode::ZombieSighting;
    [data getBytes:raw length:len];
    zombie->ParseFromArray(raw, len);
    return zombie;
}

@end
编辑:我正在使用Xcode 4.5。即使我按照所有步骤操作,我仍然遇到了链接器错误。

无法找到适用于i386架构的符号

由于这个原因,我无法在模拟器上运行代码。但它在实际设备上可用。


这是一篇很棒的文章,但我好奇为什么人们不想使用基于metasyntactic 的 booyah端口呢?它提供了Objc对象,因此您的视图代码无需担心具有不同符号表示法和手动释放内存(如果使用arc)的c++对象。这是protobuf版本的问题吗?您需要 c++提供的反射api吗?我在多个项目中都使用booyah端口,到目前为止都运行得很好... - Ted Tomlinson
1
我在编译具有“repeated”变量的protos时遇到了问题。 - Tushar Koul
@Ted,请发布你使用的库链接。 - Tushar Koul
我也遇到了像@TusharKoul一样的“重复”变量错误,这意味着我必须使用这个C++代码。 - AlBeebe
@AlBeebe 你尝试编译Ted发布的booyah仓库里的代码了吗? - Tushar Koul

2

根据实际问题,我认为我的评论值得发布为答案:

我正在使用稍微修改过的本地Obj代码生成,由Booyah提供。

它支持开箱即用的重复字段,但为了使用ObjC快速枚举,您需要将PBArray类型(基本上是一个有类型的c缓冲区)转换为它表示的NSObject数组 - 要么是NSNumber或对象的protobuf消息。您可以在此更改中查看已更新的快速枚举代码示例:。您还可以在PBArray上添加名为toObjects的类别。

我只是使用-fno-objc-arc标记生成的代码,但您可以从booyah拉取请求获取ARC和2.5支持。

设置说明非常好,但如果人们想要更明确的说明,例如我使用的类别,如何构建protobuf-objc插件,如何获得Class前缀的支持(例如IXMyProtoMessage而不是MyProtoMessage),或者如何生成代码,请告诉我,我会尝试抽出时间写一篇文章。我正在使用它与> 50个proto文件,具有许多跨项目依赖关系。

该库的一个弱点是它不包括生成的代码上的典型Protobuf反射api,因此执行诸如将消息转换为NSDictionary之类的操作需要使用objC运行时进行一些hacky处理(代码不遵循典型的KV兼容性)或编写自定义代码生成器从具有反射api的protos中(我使用python + jinja2完成了这项工作)。或者 - 更好的方法是并且难度相似,将反射api添加到代码生成器中;)。


请问您能否分享一下如何将 PBArray 转换为 NSArray 的扩展程序? - Richard Topchii

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接