Objective-C 生成コードガイド
proto2、proto3、およびエディションによって生成されるコード間の違いが強調表示されています。このドキュメントを読む前に、proto2 言語ガイド、および/またはproto3 言語ガイド、および/またはエディションガイドを読むことをお勧めします。
コンパイラの呼び出し
プロトコルバッファコンパイラは、--objc_out= コマンドラインフラグを指定して呼び出されたときに Objective-C 出力を生成します。--objc_out= オプションのパラメータは、コンパイラが Objective-C 出力を書き込むディレクトリです。コンパイラは、各 .proto ファイル入力に対してヘッダーファイルと実装ファイルを生成します。出力ファイルの名前は、.proto ファイルの名前を取得し、以下の変更を加えて計算されます。
- ファイル名は、
.protoファイルのベース名をキャメルケースに変換することによって決定されます。たとえば、foo_bar.protoはFooBarになります。 - 拡張子 (
.proto) は、ヘッダーファイルの場合はpbobjc.hに、実装ファイルの場合はpbobjc.mにそれぞれ置き換えられます。 - プロトパス (
--proto_path=または-Iコマンドラインフラグで指定) は、出力パス (--objc_out=フラグで指定) に置き換えられます。
したがって、たとえば、コンパイラを次のように呼び出すと
protoc --proto_path=src --objc_out=build/gen src/foo.proto src/bar/baz.proto
コンパイラは src/foo.proto と src/bar/baz.proto のファイルを読み込み、4つの出力ファイルを生成します: build/gen/Foo.pbobjc.h、build/gen/Foo.pbobjc.m、build/gen/bar/Baz.pbobjc.h、および build/gen/bar/Baz.pbobjc.m。コンパイラは必要に応じてディレクトリ build/gen/bar を自動的に作成しますが、build や build/gen は作成しません。それらは既に存在している必要があります。
パッケージ
プロトコルバッファコンパイラによって生成される Objective-C コードは、Objective-C に言語によって強制される名前空間がないため、.proto ファイルで定義されたパッケージ名の影響をまったく受けません。代わりに、Objective-C クラス名はプレフィックスを使用して区別され、これについては次のセクションで説明します。
クラスプレフィックス
次のファイルオプションがある場合
option objc_class_prefix = "CGOOP";
指定された文字列(この場合は CGOOP)は、この .proto ファイル用に生成されたすべての Objective-C クラスの前にプレフィックスとして付加されます。Apple が推奨するように、3 文字以上のプレフィックスを使用してください。2 文字のプレフィックスはすべて Apple によって予約されていることに注意してください。
キャメルケース変換
イディオマティックな Objective-C は、すべての識別子にキャメルケースを使用します。
メッセージは、プロトファイルではすでにキャメルケースでメッセージに名前を付けるのが標準であるため、名前が変換されません。ユーザーは正当な理由で慣習を回避したと想定されており、実装はその意図に準拠します。
フィールド名、oneof、enum 宣言、および拡張アクセサから生成されたメソッドは、名前がキャメルケースになります。一般に、プロト名をキャメルケースの Objective-C 名に変換するには、
- 最初の文字が大文字に変換されます(常に小文字で始まるフィールドを除く)。
- 名前の各アンダースコアについて、アンダースコアは削除され、次の文字が大文字になります。
したがって、たとえば、フィールド foo_bar_baz は fooBarBaz になります。フィールド FOO_bar は fooBar になります。
メッセージ
単純なメッセージ宣言を考えます。
message Foo {}
プロトコルバッファコンパイラは Foo というクラスを生成します。objc_class_prefix ファイルオプションを指定した場合、このオプションの値が生成されたクラス名の前に付加されます。
C/C++ または Objective-C のキーワードと名前が一致する外部メッセージの場合
message static {}
生成されるインターフェースは、次のように _Class で接尾辞が付けられます。
@interface static_Class {}
なお、キャメルケース変換ルールに従い、名前 static は変換されません。キャメルケースの名前が FieldNumber または OneOfCase である内部メッセージの場合、生成されたインターフェースは、生成された名前が FieldNumber 列挙型または OneOfCase 列挙型と競合しないように、_Class で接尾辞が付けられたキャメルケースの名前になります。
メッセージは別のメッセージ内に宣言することもできます。
message Foo {
message Bar {}
}
これにより、
@interface Foo_Bar : GPBMessage
@end
ご覧のとおり、生成されたネストされたメッセージ名は、生成されたコンテナメッセージ名(Foo)にアンダースコア(_)とネストされたメッセージ名(Bar)を付加したものです。
注: 衝突を最小限に抑えるよう努力していますが、アンダースコアとキャメルケースの変換により、メッセージ名が衝突する可能性がまだあります。例として、
message foo_bar {} message foo { message bar {} }はどちらも
@interface foo_barを生成し、衝突します。最も実用的な解決策は、競合するメッセージの名前を変更することかもしれません。
GPBMessage インターフェース
GPBMessage は、生成されたすべてのメッセージクラスのスーパークラスです。以下のインターフェイスのスーパーセットをサポートする必要があります。
@interface GPBMessage : NSObject
@end
このインターフェースの動作は次のとおりです。
// Will do a deep copy.
- (id)copy;
// Will perform a deep equality comparison.
- (BOOL)isEqual:(id)value;
不明なフィールド
メッセージがパースされると、パースコードに不明なフィールドが含まれている場合があります。これは、メッセージが古いバージョンの .proto 定義で作成され、新しいバージョンから生成されたコードでパースされた場合 (またはその逆の場合) に発生する可能性があります。
これらのフィールドは破棄されず、メッセージの unknownFields プロパティに格納されます。
@property(nonatomic, copy, nullable) GPBUnknownFieldSet *unknownFields;
GPBUnknownFieldSet インターフェースを使用して、これらのフィールドを番号で取得したり、配列としてループしたりできます。
フィールド
以下のセクションでは、プロトコルバッファコンパイラによってメッセージフィールド用に生成されるコードについて説明します。これらは、暗黙的な存在と明示的な存在によって分けられています。この区別については、フィールドの存在で詳しく学ぶことができます。
暗黙的なプレゼンスを持つ単数フィールド
すべての単一フィールドに対して、コンパイラはデータを格納するプロパティと、フィールド番号を含む整数定数を生成します。メッセージ型フィールドには、エンコードされたメッセージでフィールドが設定されているかどうかを確認できる has.. プロパティも付与されます。たとえば、次のメッセージが与えられた場合、
message Foo {
message Bar {
int32 int32_value = 1;
}
enum Qux {...}
int32 int32_value = 1;
string string_value = 2;
Bar message_value = 3;
Qux enum_value = 4;
bytes bytes_value = 5;
}
コンパイラは以下を生成します。
typedef GPB_ENUM(Foo_Bar_FieldNumber) {
// The generated field number name is the enclosing message names delimited by
// underscores followed by "FieldNumber", followed by the field name
// camel cased.
Foo_Bar_FieldNumber_Int32Value = 1,
};
@interface Foo_Bar : GPBMessage
@property(nonatomic, readwrite) int32_t int32Value;
@end
typedef GPB_ENUM(Foo_FieldNumber) {
Foo_FieldNumber_Int32Value = 1,
Foo_FieldNumber_StringValue = 2,
Foo_FieldNumber_MessageValue = 3,
Foo_FieldNumber_EnumValue = 4,
Foo_FieldNumber_BytesValue = 5,
};
typedef GPB_ENUM(Foo_Qux) {
Foo_Qux_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
...
};
@interface Foo : GPBMessage
// Field names are camel cased.
@property(nonatomic, readwrite) int32_t int32Value;
@property(nonatomic, readwrite, copy, null_resettable) NSString *stringValue;
@property(nonatomic, readwrite) BOOL hasMessageValue;
@property(nonatomic, readwrite, strong, null_resettable) Foo_Bar *messageValue;
@property(nonatomic, readwrite) Foo_Qux enumValue;
@property(nonatomic, readwrite, copy, null_resettable) NSData *bytesValue;
@end
特殊な名前付けケース
フィールド名の生成ルールによって名前の競合が発生し、名前を「一意」にする必要がある場合があります。そのような競合は、フィールドの末尾に _p を追加することで解決されます(_p は非常に一意で、「プロパティ」の略であるため選択されました)。
message Foo {
int32 foo_array = 1; // Ends with Array
int32 bar_OneOfCase = 2; // Ends with oneofcase
int32 id = 3; // Is a C/C++/Objective-C keyword
}
生成する
typedef GPB_ENUM(Foo_FieldNumber) {
// If a non-repeatable field name ends with "Array" it will be suffixed
// with "_p" to keep the name distinct from repeated types.
Foo_FieldNumber_FooArray_p = 1,
// If a field name ends with "OneOfCase" it will be suffixed with "_p" to
// keep the name distinct from OneOfCase properties.
Foo_FieldNumber_BarOneOfCase_p = 2,
// If a field name is a C/C++/ObjectiveC keyword it will be suffixed with
// "_p" to allow it to compile.
Foo_FieldNumber_Id_p = 3,
};
@interface Foo : GPBMessage
@property(nonatomic, readwrite) int32_t fooArray_p;
@property(nonatomic, readwrite) int32_t barOneOfCase_p;
@property(nonatomic, readwrite) int32_t id_p;
@end
デフォルト値
数値型のデフォルト値は 0 です。
文字列のデフォルト値は @"" であり、バイトのデフォルト値は [NSData data] です。
文字列フィールドに nil を割り当てると、デバッグビルドではアサートされ、リリースビルドではフィールドが @"" に設定されます。バイトフィールドに nil を割り当てると、デバッグビルドではアサートされ、リリースビルドではフィールドが [NSData data] に設定されます。バイトまたは文字列フィールドが設定されているかどうかをテストするには、その長さプロパティをテストし、0と比較する必要があります。
メッセージのデフォルトの「空」の値は、デフォルトメッセージのインスタンスです。メッセージ値をクリアするには、nil に設定する必要があります。クリアされたメッセージにアクセスすると、デフォルトメッセージのインスタンスが返され、hasFoo メソッドは false を返します。
フィールドに対して返されるデフォルトメッセージはローカルインスタンスです。nil の代わりにデフォルトメッセージを返す理由は、次の場合に、
message Foo {
message Bar {
int32 b;
}
Bar a;
}
実装は以下をサポートします。
Foo *foo = [[Foo alloc] init];
foo.a.b = 2;
ここで、a は必要に応じてアクセサーを介して自動的に作成されます。foo.a が nil を返した場合、foo.a.b セッターパターンは機能しません。
明示的なプレゼンスを持つ単数フィールド
すべての単一フィールドに対して、コンパイラはデータを格納するプロパティ、フィールド番号を含む整数定数、およびエンコードされたメッセージでフィールドが設定されているかどうかを確認できる has.. プロパティを生成します。たとえば、次のメッセージが与えられた場合、
message Foo {
message Bar {
int32 int32_value = 1;
}
enum Qux {...}
optional int32 int32_value = 1;
optional string string_value = 2;
optional Bar message_value = 3;
optional Qux enum_value = 4;
optional bytes bytes_value = 5;
}
コンパイラは以下を生成します。
# Enum Foo_Qux
typedef GPB_ENUM(Foo_Qux) {
Foo_Qux_Flupple = 0,
};
GPBEnumDescriptor *Foo_Qux_EnumDescriptor(void);
BOOL Foo_Qux_IsValidValue(int32_t value);
# Message Foo
typedef GPB_ENUM(Foo_FieldNumber) {
Foo_FieldNumber_Int32Value = 2,
Foo_FieldNumber_MessageValue = 3,
Foo_FieldNumber_EnumValue = 4,
Foo_FieldNumber_BytesValue = 5,
Foo_FieldNumber_StringValue = 6,
};
@interface Foo : GPBMessage
@property(nonatomic, readwrite) BOOL hasInt32Value;
@property(nonatomic, readwrite) int32_t int32Value;
@property(nonatomic, readwrite) BOOL hasStringValue;
@property(nonatomic, readwrite, copy, null_resettable) NSString *stringValue;
@property(nonatomic, readwrite) BOOL hasMessageValue;
@property(nonatomic, readwrite, strong, null_resettable) Foo_Bar *messageValue;
@property(nonatomic, readwrite) BOOL hasEnumValue;
@property(nonatomic, readwrite) Foo_Qux enumValue;
@property(nonatomic, readwrite) BOOL hasBytesValue;
@property(nonatomic, readwrite, copy, null_resettable) NSData *bytesValue;
@end
# Message Foo_Bar
typedef GPB_ENUM(Foo_Bar_FieldNumber) {
Foo_Bar_FieldNumber_Int32Value = 1,
};
@interface Foo_Bar : GPBMessage
@property(nonatomic, readwrite) BOOL hasInt32Value;
@property(nonatomic, readwrite) int32_t int32Value;
@end
特殊な名前付けケース
フィールド名の生成ルールによって名前の競合が発生し、名前を「一意」にする必要がある場合があります。そのような競合は、フィールドの末尾に _p を追加することで解決されます(_p は非常に一意で、「プロパティ」の略であるため選択されました)。
message Foo {
optional int32 foo_array = 1; // Ends with Array
optional int32 bar_OneOfCase = 2; // Ends with oneofcase
optional int32 id = 3; // Is a C/C++/Objective-C keyword
}
生成する
typedef GPB_ENUM(Foo_FieldNumber) {
// If a non-repeatable field name ends with "Array" it will be suffixed
// with "_p" to keep the name distinct from repeated types.
Foo_FieldNumber_FooArray_p = 1,
// If a field name ends with "OneOfCase" it will be suffixed with "_p" to
// keep the name distinct from OneOfCase properties.
Foo_FieldNumber_BarOneOfCase_p = 2,
// If a field name is a C/C++/ObjectiveC keyword it will be suffixed with
// "_p" to allow it to compile.
Foo_FieldNumber_Id_p = 3,
};
@interface Foo : GPBMessage
@property(nonatomic, readwrite) int32_t fooArray_p;
@property(nonatomic, readwrite) int32_t barOneOfCase_p;
@property(nonatomic, readwrite) int32_t id_p;
@end
デフォルト値(オプションフィールドのみ)
数値型のデフォルト値は、ユーザーによって明示的なデフォルトが指定されていない場合、0 です。
文字列のデフォルト値は @"" であり、バイトのデフォルト値は [NSData data] です。
文字列フィールドに nil を割り当てると、デバッグビルドではアサートされ、リリースビルドではフィールドが @"" に設定されます。バイトフィールドに nil を割り当てると、デバッグビルドではアサートされ、リリースビルドではフィールドが [NSData data] に設定されます。バイトまたは文字列フィールドが設定されているかどうかをテストするには、その長さプロパティをテストし、0と比較する必要があります。
メッセージのデフォルトの「空」の値は、デフォルトメッセージのインスタンスです。メッセージ値をクリアするには、nil に設定する必要があります。クリアされたメッセージにアクセスすると、デフォルトメッセージのインスタンスが返され、hasFoo メソッドは false を返します。
フィールドに対して返されるデフォルトメッセージはローカルインスタンスです。nil の代わりにデフォルトメッセージを返す理由は、次の場合に、
message Foo {
message Bar {
int32 b;
}
Bar a;
}
実装は以下をサポートします。
Foo *foo = [[Foo alloc] init];
foo.a.b = 2;
ここで、a は必要に応じてアクセサーを介して自動的に作成されます。foo.a が nil を返した場合、foo.a.b セッターパターンは機能しません。
繰り返しフィールド
単一フィールド (proto2 proto3) と同様に、プロトコルバッファコンパイラは、繰り返しフィールドごとに1つのデータプロパティを生成します。このデータプロパティは、フィールドタイプに応じて GPB<VALUE>Array であり、<VALUE> は UInt32、Int32、UInt64、Int64、Bool、Float、Double、または Enum のいずれかになります。string、bytes、message 型には NSMutableArray が使用されます。繰り返し型のフィールド名には Array が末尾に追加されます。Objective-C インターフェースで Array を追加する理由は、コードの可読性を高めるためです。プロトファイル内の繰り返しフィールドは単数形になりがちで、標準の Objective-C の使用方法では読みにくい場合があります。単数形を複数形にすることはより慣用的な Objective-C ですが、複数形のルールはコンパイラでサポートするには複雑すぎます。
message Foo {
message Bar {}
enum Qux {}
repeated int32 int32_value = 1;
repeated string string_value = 2;
repeated Bar message_value = 3;
repeated Qux enum_value = 4;
}
生成する
typedef GPB_ENUM(Foo_FieldNumber) {
Foo_FieldNumber_Int32ValueArray = 1,
Foo_FieldNumber_StringValueArray = 2,
Foo_FieldNumber_MessageValueArray = 3,
Foo_FieldNumber_EnumValueArray = 4,
};
@interface Foo : GPBMessage
// Field names for repeated types are the camel case name with
// "Array" suffixed.
@property(nonatomic, readwrite, strong, null_resettable)
GPBInt32Array *int32ValueArray;
@property(nonatomic, readonly) NSUInteger int32ValueArray_Count;
@property(nonatomic, readwrite, strong, null_resettable)
NSMutableArray *stringValueArray;
@property(nonatomic, readonly) NSUInteger stringValueArray_Count;
@property(nonatomic, readwrite, strong, null_resettable)
NSMutableArray *messageValueArray;
@property(nonatomic, readonly) NSUInteger messageValueArray_Count;
@property(nonatomic, readwrite, strong, null_resettable)
GPBEnumArray *enumValueArray;
@property(nonatomic, readonly) NSUInteger enumValueArray_Count;
@end
注: 繰り返しフィールドの動作は、エディションでfeatures.repeated_field_encoding 機能を使用して構成できます。
文字列、バイト、メッセージフィールドの場合、配列の要素はそれぞれ NSString*、NSData*、および GPBMessage のサブクラスへのポインタです。
デフォルト値
繰り返しフィールドのデフォルト値は空です。Objective-C で生成されたコードでは、これは空の GPB<VALUE>Array です。空の繰り返しフィールドにアクセスすると、他の繰り返しフィールド配列と同じように更新できる空の配列が返されます。
Foo *myFoo = [[Foo alloc] init];
[myFoo.stringValueArray addObject:@"A string"]
また、提供されている <field>Array_Count プロパティを使用して、配列を作成することなく、特定の繰り返しフィールドの配列が空であるかどうかを確認することもできます。
if (myFoo.messageValueArray_Count) {
// There is something in the array...
}
GPB<VALUE>Array インターフェース
GPB<VALUE>Array (後述する GPBEnumArray を除く) は、以下のインターフェースを持ちます。
@interface GPBArray : NSObject
@property (nonatomic, readonly) NSUInteger count;
+ (instancetype)array;
+ (instancetype)arrayWithValue:()value;
+ (instancetype)arrayWithValueArray:(GPBArray *)array;
+ (instancetype)arrayWithCapacity:(NSUInteger)count;
// Initializes the array, copying the values.
- (instancetype)initWithValueArray:(GPBArray *)array;
- (instancetype)initWithValues:(const [])values
count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCapacity:(NSUInteger)count;
- ()valueAtIndex:(NSUInteger)index;
- (void)enumerateValuesWithBlock:
(void (^)( value, NSUInteger idx, BOOL *stop))block;
- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
usingBlock:(void (^)( value, NSUInteger idx, BOOL *stop))block;
- (void)addValue:()value;
- (void)addValues:(const [])values count:(NSUInteger)count;
- (void)addValuesFromArray:(GPBArray *)array;
- (void)removeValueAtIndex:(NSUInteger)count;
- (void)removeAll;
- (void)exchangeValueAtIndex:(NSUInteger)idx1
withValueAtIndex:(NSUInteger)idx2;
- (void)insertValue:()value atIndex:(NSUInteger)count;
- (void)replaceValueAtIndex:(NSUInteger)index withValue:()value;
@end
GPBEnumArray は、検証関数を処理し、生の値をアクセスするために、わずかに異なるインターフェースを持っています。
@interface GPBEnumArray : NSObject
@property (nonatomic, readonly) NSUInteger count;
@property (nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+ (instancetype)array;
+ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func
rawValue:value;
+ (instancetype)arrayWithValueArray:(GPBEnumArray *)array;
+ (instancetype)arrayWithValidationFunction:(nullable GPBEnumValidationFunc)func
capacity:(NSUInteger)count;
- (instancetype)initWithValidationFunction:
(nullable GPBEnumValidationFunc)func;
// Initializes the array, copying the values.
- (instancetype)initWithValueArray:(GPBEnumArray *)array;
- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
values:(const int32_t [])values
count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
capacity:(NSUInteger)count;
// These will return kGPBUnrecognizedEnumeratorValue if the value at index
// is not a valid enumerator as defined by validationFunc. If the actual
// value is desired, use the "raw" version of the method.
- (int32_t)valueAtIndex:(NSUInteger)index;
- (void)enumerateValuesWithBlock:
(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
- (void)enumerateValuesWithOptions:(NSEnumerationOptions)opts
usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
// These methods bypass the validationFunc to provide access to values
// that were not known at the time the binary was compiled.
- (int32_t)rawValueAtIndex:(NSUInteger)index;
- (void)enumerateRawValuesWithBlock:
(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
- (void)enumerateRawValuesWithOptions:(NSEnumerationOptions)opts
usingBlock:(void (^)(int32_t value, NSUInteger idx, BOOL *stop))block;
// If value is not a valid enumerator as defined by validationFunc, these
// methods will assert in debug, and will log in release and assign the value
// to the default value. Use the rawValue methods below to assign
// non enumerator values.
- (void)addValue:(int32_t)value;
- (void)addValues:(const int32_t [])values count:(NSUInteger)count;
- (void)insertValue:(int32_t)value atIndex:(NSUInteger)count;
- (void)replaceValueAtIndex:(NSUInteger)index withValue:(int32_t)value;
// These methods bypass the validationFunc to provide setting of values that
// were not known at the time the binary was compiled.
- (void)addRawValue:(int32_t)rawValue;
- (void)addRawValuesFromEnumArray:(GPBEnumArray *)array;
- (void)addRawValues:(const int32_t [])values count:(NSUInteger)count;
- (void)replaceValueAtIndex:(NSUInteger)index withRawValue:(int32_t)rawValue;
- (void)insertRawValue:(int32_t)value atIndex:(NSUInteger)count;
// No validation applies to these methods.
- (void)removeValueAtIndex:(NSUInteger)count;
- (void)removeAll;
- (void)exchangeValueAtIndex:(NSUInteger)idx1
withValueAtIndex:(NSUInteger)idx2;
@end
Oneof フィールド
oneof フィールド定義を含むメッセージが与えられた場合
message Order {
oneof OrderID {
string name = 1;
int32 address = 2;
};
int32 quantity = 3;
};
プロトコルバッファコンパイラが生成する
typedef GPB_ENUM(Order_OrderID_OneOfCase) {
Order_OrderID_OneOfCase_GPBUnsetOneOfCase = 0,
Order_OrderID_OneOfCase_Name = 1,
Order_OrderID_OneOfCase_Address = 2,
};
typedef GPB_ENUM(Order_FieldNumber) {
Order_FieldNumber_Name = 1,
Order_FieldNumber_Address = 2,
Order_FieldNumber_Quantity = 3,
};
@interface Order : GPBMessage
@property (nonatomic, readwrite) Order_OrderID_OneOfCase orderIDOneOfCase;
@property (nonatomic, readwrite, copy, null_resettable) NSString *name;
@property (nonatomic, readwrite) int32_t address;
@property (nonatomic, readwrite) int32_t quantity;
@end
void Order_ClearOrderIDOneOfCase(Order *message);
oneof プロパティのいずれかを設定すると、oneof に関連付けられた他のすべてのプロパティがクリアされます。
<ONE_OF_NAME>_OneOfCase_GPBUnsetOneOfCase は、oneof のいずれかのフィールドが設定されているかどうかを簡単にテストできるように、常に 0 と等しくなります。
マップフィールド
このメッセージ定義の場合
message Bar {...}
message Foo {
map<int32, string> a_map = 1;
map<string, Bar> b_map = 2;
};
コンパイラは以下を生成します。
typedef GPB_ENUM(Foo_FieldNumber) {
Foo_FieldNumber_AMap = 1,
Foo_FieldNumber_BMap = 2,
};
@interface Foo : GPBMessage
// Map names are the camel case version of the field name.
@property (nonatomic, readwrite, strong, null_resettable) GPBInt32ObjectDictionary *aMap;
@property(nonatomic, readonly) NSUInteger aMap_Count;
@property (nonatomic, readwrite, strong, null_resettable) NSMutableDictionary *bMap;
@property(nonatomic, readonly) NSUInteger bMap_Count;
@end
キーが文字列で、値が文字列、バイト、またはメッセージである場合は、NSMutableDictionary によって処理されます。
その他のケースは次のとおりです。
GBP<KEY><VALUE>Dictionary
ここで
<KEY>は Uint32、Int32、UInt64、Int64、Bool、または String です。<VALUE>は UInt32、Int32、UInt64、Int64、Bool、Float、Double、Enum、または Object です。Objectは、クラス数を減らし、Objective-C がNSMutableDictionaryを扱う方法に沿うために、型stringbytesまたはmessageの値に使用されます。
デフォルト値
マップフィールドのデフォルト値は空です。Objective-C で生成されたコードでは、これは空の GBP<KEY><VALUE>Dictionary です。空のマップフィールドにアクセスすると、他のマップフィールドと同じように更新できる空の辞書が返されます。
また、提供されている <mapField>_Count プロパティを使用して、特定のマップが空であるかどうかを確認することもできます。
if (myFoo.myMap_Count) {
// There is something in the map...
}
GBP<KEY><VALUE>Dictionary インターフェース
GBP<KEY><VALUE>Dictionary (GBP<KEY>ObjectDictionary および GBP<KEY>EnumDictionary を除く) インターフェースは以下のとおりです。
@interface GPB<KEY>Dictionary : NSObject
@property (nonatomic, readonly) NSUInteger count;
+ (instancetype)dictionary;
+ (instancetype)dictionaryWithValue:(const )value
forKey:(const <KEY>)key;
+ (instancetype)dictionaryWithValues:(const [])values
forKeys:(const <KEY> [])keys
count:(NSUInteger)count;
+ (instancetype)dictionaryWithDictionary:(GPB<KEY>Dictionary *)dictionary;
+ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
- (instancetype)initWithValues:(const [])values
forKeys:(const <KEY> [])keys
count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithDictionary:(GPB<KEY>Dictionary *)dictionary;
- (instancetype)initWithCapacity:(NSUInteger)numItems;
- (BOOL)valueForKey:(<KEY>)key value:(VALUE *)value;
- (void)enumerateKeysAndValuesUsingBlock:
(void (^)(<KEY> key, value, BOOL *stop))block;
- (void)removeValueForKey:(<KEY>)aKey;
- (void)removeAll;
- (void)setValue:()value forKey:(<KEY>)key;
- (void)addEntriesFromDictionary:(GPB<KEY>Dictionary *)otherDictionary;
@end
GBP<KEY>ObjectDictionary インターフェースは、
@interface GPB<KEY>ObjectDictionary : NSObject
@property (nonatomic, readonly) NSUInteger count;
+ (instancetype)dictionary;
+ (instancetype)dictionaryWithObject:(id)object
forKey:(const <KEY>)key;
+ (instancetype)
dictionaryWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
forKeys:(const <KEY> [])keys
count:(NSUInteger)count;
+ (instancetype)dictionaryWithDictionary:(GPB<KEY>ObjectDictionary *)dictionary;
+ (instancetype)dictionaryWithCapacity:(NSUInteger)numItems;
- (instancetype)initWithObjects:(const id GPB_UNSAFE_UNRETAINED [])objects
forKeys:(const <KEY> [])keys
count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithDictionary:(GPB<KEY>ObjectDictionary *)dictionary;
- (instancetype)initWithCapacity:(NSUInteger)numItems;
- (id)objectForKey:(uint32_t)key;
- (void)enumerateKeysAndObjectsUsingBlock:
(void (^)(<KEY> key, id object, BOOL *stop))block;
- (void)removeObjectForKey:(<KEY>)aKey;
- (void)removeAll;
- (void)setObject:(id)object forKey:(<KEY>)key;
- (void)addEntriesFromDictionary:(GPB<KEY>ObjectDictionary *)otherDictionary;
@end
GBP<KEY>EnumDictionary は、検証関数を処理し、生の値をアクセスするために、わずかに異なるインターフェースを持っています。
@interface GPB<KEY>EnumDictionary : NSObject
@property(nonatomic, readonly) NSUInteger count;
@property(nonatomic, readonly) GPBEnumValidationFunc validationFunc;
+ (instancetype)dictionary;
+ (instancetype)dictionaryWithValidationFunction:(nullable GPBEnumValidationFunc)func;
+ (instancetype)dictionaryWithValidationFunction:(nullable GPBEnumValidationFunc)func
rawValue:(int32_t)rawValue
forKey:(<KEY>_t)key;
+ (instancetype)dictionaryWithValidationFunction:(nullable GPBEnumValidationFunc)func
rawValues:(const int32_t [])values
forKeys:(const <KEY>_t [])keys
count:(NSUInteger)count;
+ (instancetype)dictionaryWithDictionary:(GPB<KEY>EnumDictionary *)dictionary;
+ (instancetype)dictionaryWithValidationFunction:(nullable GPBEnumValidationFunc)func
capacity:(NSUInteger)numItems;
- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func;
- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
rawValues:(const int32_t [])values
forKeys:(const <KEY>_t [])keys
count:(NSUInteger)count NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithDictionary:(GPB<KEY>EnumDictionary *)dictionary;
- (instancetype)initWithValidationFunction:(nullable GPBEnumValidationFunc)func
capacity:(NSUInteger)numItems;
// These will return kGPBUnrecognizedEnumeratorValue if the value for the key
// is not a valid enumerator as defined by validationFunc. If the actual value is
// desired, use "raw" version of the method.
- (BOOL)valueForKey:(<KEY>_t)key value:(nullable int32_t *)value;
- (void)enumerateKeysAndValuesUsingBlock:
(void (^)(<KEY>_t key, int32_t value, BOOL *stop))block;
// These methods bypass the validationFunc to provide access to values that were not
// known at the time the binary was compiled.
- (BOOL)valueForKey:(<KEY>_t)key rawValue:(nullable int32_t *)rawValue;
- (void)enumerateKeysAndRawValuesUsingBlock:
(void (^)(<KEY>_t key, int32_t rawValue, BOOL *stop))block;
- (void)addRawEntriesFromDictionary:(GPB<KEY>EnumDictionary *)otherDictionary;
// If value is not a valid enumerator as defined by validationFunc, these
// methods will assert in debug, and will log in release and assign the value
// to the default value. Use the rawValue methods below to assign non enumerator
// values.
- (void)setValue:(int32_t)value forKey:(<KEY>_t)key;
// This method bypass the validationFunc to provide setting of values that were not
// known at the time the binary was compiled.
- (void)setRawValue:(int32_t)rawValue forKey:(<KEY>_t)key;
// No validation applies to these methods.
- (void)removeValueForKey:(<KEY>_t)aKey;
- (void)removeAll;
@end
列挙型
以下のようなenum定義が与えられた場合
enum Foo {
VALUE_A = 0;
VALUE_B = 1;
VALUE_C = 5;
}
生成されたコードは次のようになります。
// The generated enum value name will be the enumeration name followed by
// an underscore and then the enumerator name converted to camel case.
// GPB_ENUM is a macro defined in the Objective-C Protocol Buffer headers
// that enforces all enum values to be int32 and aids in Swift Enumeration
// support.
typedef GPB_ENUM(Foo) {
Foo_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue, //proto3 only
Foo_ValueA = 0,
Foo_ValueB = 1;
Foo_ValueC = 5;
};
// Returns information about what values this enum type defines.
GPBEnumDescriptor *Foo_EnumDescriptor();
各列挙型には、そのための検証関数が宣言されています。
// Returns YES if the given numeric value matches one of Foo's
// defined values (0, 1, 5).
BOOL Foo_IsValidValue(int32_t value);
そして、そのための列挙子記述子アクセサ関数が宣言されています。
// GPBEnumDescriptor is defined in the runtime and contains information
// about the enum definition, such as the enum name, enum value and enum value
// validation function.
typedef GPBEnumDescriptor *(*GPBEnumDescriptorAccessorFunc)();
列挙子記述子アクセサ関数は、列挙型クラスのメソッドではなくC関数です。これは、クライアントソフトウェアによってほとんど使用されないためです。これにより、生成されるObjective-Cランタイム情報の量が削減され、リンカーがそれらをデッドストリップできる可能性があります。
C/C++ または Objective-C のキーワードと名前が一致する外部列挙型の場合、たとえば、
enum method {}
生成されたインターフェースは、次のように _Enum で接尾辞が付けられます。
// The generated enumeration name is the keyword suffixed by _Enum.
typedef GPB_ENUM(Method_Enum) {}
列挙型は別のメッセージ内でも宣言できます。たとえば、
message Foo {
enum Bar {
VALUE_A = 0;
VALUE_B = 1;
VALUE_C = 5;
}
Bar aBar = 1;
Bar aDifferentBar = 2;
repeated Bar aRepeatedBar = 3;
}
生成する
typedef GPB_ENUM(Foo_Bar) {
Foo_Bar_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue, //proto3 only
Foo_Bar_ValueA = 0;
Foo_Bar_ValueB = 1;
Foo_Bar_ValueC = 5;
};
GPBEnumDescriptor *Foo_Bar_EnumDescriptor();
BOOL Foo_Bar_IsValidValue(int32_t value);
@interface Foo : GPBMessage
@property (nonatomic, readwrite) Foo_Bar aBar;
@property (nonatomic, readwrite) Foo_Bar aDifferentBar;
@property (nonatomic, readwrite, strong, null_resettable)
GPBEnumArray *aRepeatedBarArray;
@end
// proto3 only Every message that has an enum field will have an accessor function to get
// the value of that enum as an integer. This allows clients to deal with
// raw values if they need to.
int32_t Foo_ABar_RawValue(Foo *message);
void SetFoo_ABar_RawValue(Foo *message, int32_t value);
int32_t Foo_ADifferentBar_RawValue(Foo *message);
void SetFoo_ADifferentBar_RawValue(Foo *message, int32_t value);
すべての列挙フィールドは、型付き列挙子 (上記の例では Foo_Bar) として値にアクセスする機能、または proto3 を使用している場合は生 int32_t 値 (上記の例のアクセサー関数を使用) として値にアクセスする機能を持っています。これは、クライアントとサーバーが異なるバージョンのプロトファイルでコンパイルされているために、サーバーがクライアントが認識できない値を返すケースをサポートするためです。
認識されない列挙型値は、言語バージョンと Editions の features.enum_type 機能に応じて異なる方法で処理されます。
- オープン列挙型では、解析されたメッセージデータの列挙型値が、コードがサポートするようにコンパイルされたものでない場合、型付き列挙型値として
kGPBUnrecognizedEnumeratorValueが返されます。実際の値が必要な場合は、生のアクセサーを使用してint32_tとして値を取得します。 - クローズド列挙型では、認識されない列挙型値は不明なフィールドとして扱われます。
- Proto2 の列挙型はクローズドであり、proto3 の列挙型はオープンです。Editions では、動作は
features.enum_type機能で構成可能です。
kGPBUnrecognizedEnumeratorValue は 0xFBADBEEF として定義されており、列挙型内のいずれかの列挙子がこの値を持っている場合はエラーになります。この値に列挙型フィールドを設定しようとすると、ランタイムエラーになります。同様に、型付きアクセサを使用して、その列挙型で定義されていない列挙子に列挙型フィールドを設定しようとすると、ランタイムエラーになります。どちらのエラーケースでも、デバッグビルドではアサーションが発生し、リリースビルドではログが記録され、フィールドはデフォルト値 (0) に設定されます。
生のアクセサ関数は、ほとんどの場合使用されないため、Objective-C メソッドではなく C 関数として定義されています。C 関数として宣言することで、無駄な Objective-C ランタイム情報が削減され、リンカーがそれらをデッドストリップする可能性が高まります。
Swift 列挙型のサポート
Apple は、C API との対話で Objective-C 列挙型を Swift 列挙型にインポートする方法を文書化しています。プロトコルバッファによって生成される列挙型は、Objective-C から Swift への変換をサポートしています。
// Proto
enum Foo {
VALUE_A = 0;
}
生成する
// Objective-C
typedef GPB_ENUM(Foo) {
Foo_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
Foo_ValueA = 0,
};
これは Swift コードでは、
// Swift
let aValue = Foo.ValueA
let anotherValue: Foo = .GPBUnrecognizedEnumeratorValue
Well-known タイプ
プロトコルバッファで提供されるメッセージ型を使用する場合、通常、生成された Objective-C コードではプロト定義がそのまま使用されますが、使用を簡単にするためにいくつかの基本的な変換メソッドがカテゴリとして提供されています。ただし、Any を含むすべての well-known 型に対してまだ特別な API が用意されているわけではありません(現在、Any のメッセージ値を適切な型のメッセージに変換するヘルパーメソッドはありません)。
タイムスタンプ
@interface GPBTimeStamp (GPBWellKnownTypes)
@property (nonatomic, readwrite, strong) NSDate *date;
@property (nonatomic, readwrite) NSTimeInterval timeIntervalSince1970;
- (instancetype)initWithDate:(NSDate *)date;
- (instancetype)initWithTimeIntervalSince1970:
(NSTimeInterval)timeIntervalSince1970;
@end
Duration
@interface GPBDuration (GPBWellKnownTypes)
@property (nonatomic, readwrite) NSTimeInterval timeIntervalSince1970;
- (instancetype)initWithTimeIntervalSince1970:
(NSTimeInterval)timeIntervalSince1970;
@end
拡張機能 (proto2 とエディションのみ)
拡張範囲を持つメッセージが与えられた場合
message Foo {
extensions 100 to 199;
}
extend Foo {
int32 foo = 101;
repeated int32 repeated_foo = 102;
}
message Bar {
extend Foo {
int32 bar = 103;
repeated int32 repeated_bar = 104;
}
}
コンパイラは以下を生成します。
# File Test2Root
@interface Test2Root : GPBRootObject
// The base class provides:
// + (GPBExtensionRegistry *)extensionRegistry;
// which is an GPBExtensionRegistry that includes all the extensions defined by
// this file and all files that it depends on.
@end
@interface Test2Root (DynamicMethods)
+ (GPBExtensionDescriptor *)foo;
+ (GPBExtensionDescriptor *)repeatedFoo;
@end
# Message Foo
@interface Foo : GPBMessage
@end
# Message Bar
@interface Bar : GPBMessage
@end
@interface Bar (DynamicMethods)
+ (GPBExtensionDescriptor *)bar;
+ (GPBExtensionDescriptor *)repeatedBar;
@end
これらの拡張フィールドを取得および設定するには、以下を使用します。
Foo *fooMsg = [[Foo alloc] init];
// Set the single field extensions
[fooMsg setExtension:[Test2Root foo] value:@5];
NSAssert([fooMsg hasExtension:[Test2Root foo]]);
NSAssert([[fooMsg getExtension:[Test2Root foo]] intValue] == 5);
// Add two things to the repeated extension:
[fooMsg addExtension:[Test2Root repeatedFoo] value:@1];
[fooMsg addExtension:[Test2Root repeatedFoo] value:@2];
NSAssert([fooMsg hasExtension:[Test2Root repeatedFoo]]);
NSAssert([[fooMsg getExtension:[Test2Root repeatedFoo]] count] == 2);
// Clearing
[fooMsg clearExtension:[Test2Root foo]];
[fooMsg clearExtension:[Test2Root repeatedFoo]];
NSAssert(![fooMsg hasExtension:[Test2Root foo]]);
NSAssert(![fooMsg hasExtension:[Test2Root repeatedFoo]]);