我想在iOS项目中使用Protocol Buffers。我试图避免将整个项目变成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
,{{1} }),理想情况下我想将它放在父fromString
类上。遗憾的是,我没有成功完成这项工作,因为我无法找到父类知道其子级所使用的类的方法,例如ProtobufMesssage
和init
中所需的类。
我尝试过的事情
我遇到的障碍
fromString
或Message
)的引用,因为它们是抽象的正如我所说,我对C ++或Objective-C的理解很少;我的大多数经验都是使用更高级别的语言,如Python和Java(虽然我基本上理解基本的C语言,如指针)。
这可能甚至不可能吗?我接近错误或遗漏了一些明显的东西吗?任何帮助将非常感激。感谢。
答案 0 :(得分:0)
我根本不太了解C ++,但你不能将Objective-C属性声明为Message *
吗?
您已经通过声明.mm文件中的属性将C ++代码与标头分开了,您将遇到的问题是编译器命名的实例方法(value()
和set_value()
)并且只是子类的有效方法。使用Reflection
类来获取和设置字段名称可能会有所帮助。以下是Google 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
的情况下的密钥为{{1} })。您的子类甚至不需要知道C ++代码。
您还可以将@"value"
和-init
方法中的创建者功能分隔为+fromString:
;
+_createNewInstance
允许+(Message*)_createNewInstance{ return new MessageA(); }
的子类重用除创建C ++对象之外的所有代码。
答案 1 :(得分:0)
虽然Objective C具有非常强大的instrospection功能,但C ++更受限制。你有RTTI(运行时类型信息),但它甚至没有Objective C对应的那么强大。
然而,这对你来说可能已经足够了。在Objective C ++类中,您可能会找到带有typeid运算符的消息对象的类型:
if( (typeid(self.message) == typed(foo)){
//doSomething
else if( (typeid(self.message) == typed(bar)){
// doSomething else
}
也许最好的选择是添加另一个间接级别。创建一个Objective C类层次结构,它包装所有协议缓冲区C ++类,然后创建另一个使用这些类的Objective C(可能是委托)。我相信这可能是一个更好的选择。仅在那些不可避免的情况下使用C ++。
祝你好运!