6

我想在 iOS 项目中使用协议缓冲区。我试图避免将整个项目变成 Objective-C++ 的惨败,所以我想将 C++ protobuf 类包装成 Objective-C 类。我有几十条 protobuf 消息,虽然我一次成功地完成了一个类,但理想情况下,我想使用继承来最小化重复代码。我是 Objective-C 的新手,我在 10 年里没有使用过我对 C++ 所知甚少的东西,所以这主要是一种沮丧的练习。下面是我如何包装单个消息的示例。

代码

.proto:

message MessageA {
    optional string value = 1;
}

MessageAWrapper.h:

#import <Foundation/Foundation.h>

@interface MessageAWrapper : NSObject

@property (nonatomic) NSString *value;

+ (id)fromString:(NSString *)string;
- (NSString *)serialize;

@end

MessageAWrapper.mm:

#import "MessageA.h"
#import "message.pb.h"

@interface MessageAWrapper ()

@property (nonatomic) MessageA *message;

@end

@implementation MessageAWrapper

- (id)init
{
    self = [super init];
    if (self) {
        self.message = new MessageA();
    }
    return self;
}

- (void)dealloc {
    delete self.message;
    self.message = NULL;
}

- (NSString *)value {
    return [NSString stringWithUTF8String:self.message->value().c_str()];
}

- (void)setValue:(NSString *)value {
   self.message->set_value([value UTF8String]);
}

- (NSString *)serialize {
    std::string output;
    self.message->SerializeToString(&output);
    return [NSString stringWithUTF8String:output.c_str()];
}

+ (id)fromString:(NSString *)string {
    MessageA *message = new MessageA();
    message->ParseFromString([string UTF8String]);

    MessageAWrapper *wrapper = [[MessageAWrapper alloc] init];
    wrapper.message = message;
    return wrapper;
}

@end

目标

这里有很多代码会重复几十次,其中唯一的变化是包装的类类型(init, dealloc, serialize, fromString),所以理想情况下我想把它放在父ProtobufMesssage类上。不幸的是,我没有成功完成这项工作,因为我找不到让父类知道其子类正在使用的类的方法,例如在initfromString.

我尝试过的事情

  • 结构
  • 模板类
  • 空白*

我遇到的障碍

  • 找不到存储对类/类型的引用的方法
  • .h 文件中不能有任何 C++ 头文件或代码(因为这要求整个项目都是 Objective-C++)
  • 难以保持对 protobuf 消息父级(MessageMessageLite)的引用,因为它们是抽象的

正如我所说,我对 C++ 或 Objective-C 知之甚少。我的大部分经验是使用 Python 和 Java 等更高级的语言(尽管我主要了解基本的 C 语言,如指针)。

这可能甚至不可能吗?我是在错误地接近它还是遗漏了一些明显的东西?任何帮助将非常感激。谢谢。

4

2 回答 2

0

虽然 Objective C 具有非常强大的自省功能,但 C++ 的局限性更大。您确实有RTTI(运行时类型信息),但它甚至不如 Objective C 对应的强大。

但是,这对您来说可能就足够了。在您的 Objective C++ 类中,您可能会使用 typeid 运算符找到消息对象的类型:

if( (typeid(self.message) == typed(foo)){
  //doSomething
else if( (typeid(self.message) == typed(bar)){
  // doSomething else
}

也许最好的选择是添加另一个间接级别。创建一个包含所有协议缓冲区 C++ 类的 Objective C 类层次结构,然后创建另一个使用这些类的 Objective C(可能作为委托)。我相信这可能是一个更好的选择。仅在那些不可避免的情况下使用 C++。

祝你好运!

于 2013-08-26T20:51:50.090 回答
0

我对 C++ 知之甚少,但你不能将 Objective-C 属性声明为 aMessage *吗?

您已经通过在 .mm 文件中声明属性将 C++ 代码与标头分开,您将遇到的问题是编译器 (value()set_value()) 命名的实例方法,并且仅是子类的有效方法。Reflection使用该类按名称获取和设置字段可能会有所帮助。以下是谷歌展示的摘录message.h

Message* foo = new Foo;
const Descriptor* descriptor = foo->GetDescriptor();

const FieldDescriptor* text_field = descriptor->FindFieldByName("text");
assert(text_field != NULL);
assert(text_field->type() == FieldDescriptor::TYPE_STRING);
assert(text_field->label() == FieldDescriptor::LABEL_OPTIONAL);

const Reflection* reflection = foo->GetReflection();
assert(reflection->GetString(foo, text_field) == "Hello World!");

您可以创建类型检查和获取或设置值的 Objective-C-objectForKey:-setObject:forKey:实例方法(令人困惑的是,在这种情况下的键MessageAWrapper@"value")。您的子类甚至不需要知道 C++ 代码。

您还可以将创建者函数-init+fromString:方法分离成类似的东西,+_createNewInstance;

+(Message*)_createNewInstance{ return new MessageA(); }

允许您的子类MessageWrapper重用除创建 C++ 对象之外的所有代码。

于 2013-02-21T05:46:03.530 回答