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 パス (
--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 では、すべての識別子にキャメルケースを使用します。
メッセージ名はすでにキャメルケースで命名するのが proto ファイルの標準であるため、変換されません。ユーザーが正当な理由で慣例をバイパスしたと見なされ、実装はユーザーの意図に従います。
フィールド名および oneof
、enum
宣言、および拡張機能アクセサから生成されたメソッドは、キャメルケースになります。一般に、proto 名からキャメルケースの 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;
不明なフィールド (proto2 のみ)
古いバージョン の .proto 定義で作成されたメッセージが、新しいバージョンから生成されたコード (またはその逆) で解析された場合、メッセージには「新しい」コードが認識しないオプションまたは繰り返しフィールドが含まれている可能性があります。proto2 で生成されたコードでは、これらのフィールドは破棄されず、メッセージの unknownFields
プロパティに保存されます。
@property(nonatomic, copy, nullable) GPBUnknownFieldSet *unknownFields;
GPBUnknownFieldSet
インターフェースを使用して、これらのフィールドを番号でフェッチしたり、配列としてループ処理したりできます。
proto3 では、不明なフィールドはメッセージが解析されると単純に破棄されます。
フィールド
次のセクションでは、メッセージフィールドに対してプロトコルバッファコンパイラによって生成されるコードについて説明します。
単数フィールド (proto3)
すべての単数フィールドに対して、コンパイラはデータを格納するプロパティと、フィールド番号を含む整数定数を生成します。メッセージ型フィールドには、エンコードされたメッセージでフィールドが設定されているかどうかを確認できる 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]
に設定されます。バイトフィールドまたは文字列フィールドが設定されているかどうかをテストするには、その length プロパティをテストし、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)
すべての単数フィールドに対して、コンパイラはデータを格納するプロパティ、フィールド番号を含む整数定数、およびエンコードされたメッセージでフィールドが設定されているかどうかを確認できる 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]
に設定されます。バイトフィールドまたは文字列フィールドが設定されているかどうかをテストするには、その length プロパティをテストし、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
のいずれかになります。NSMutableArray
は、string
、bytes
、および message
型に使用されます。繰り返し型のフィールド名には、Array
が付加されます。Objective-C インターフェースで Array
を付加する理由は、コードを読みやすくするためです。proto ファイルの繰り返しフィールドは、標準的な 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
string、bytes、および message フィールドの場合、配列の要素はそれぞれ 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 プロパティの 1 つを設定すると、oneof に関連付けられた他のすべてのプロパティがクリアされます。
<ONE_OF_NAME>_OneOfCase_GPBUnsetOneOfCase
は常に 0 と同等になり、oneof のフィールドが設定されているかどうかを簡単にテストできます。
マップフィールド
このメッセージ定義の場合:
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
は、クラスの数を減らすためにstring
bytes
またはmessage
型の値に使用され、Objective-C がNSMutableDictionary
で動作する方法と一致しています。
デフォルト値
マップフィールドの デフォルト値 は空です。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 の場合:
enum method {}
生成されたインターフェースには、次のように _Enum
が接尾辞として付加されます:
// The generated enumeration name is the keyword suffixed by _Enum.
typedef GPB_ENUM(Method_Enum) {}
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
値として (上記の例のアクセサ関数を使用) アクセスしたりできます。これは、クライアントとサーバーが異なるバージョンの proto ファイルでコンパイルされているために、サーバーがクライアントが認識できない値を返す場合をサポートするためです。
認識されない enum 値は、使用しているプロトコルバッファのバージョンによって異なる方法で処理されます。proto3 では、解析されたメッセージデータの列挙型値が、それを読み取るコードがコンパイルされたときにサポートするようにコンパイルされていない場合、型付き列挙子値に対して kGPBUnrecognizedEnumeratorValue
が返されます。実際の値が必要な場合は、生の値アクセサを使用して値を int32_t
として取得します。proto2 を使用している場合、認識されない enum 値は不明なフィールドとして扱われます。
kGPBUnrecognizedEnumeratorValue
は 0xFBADBEEF
として定義されており、列挙型の列挙子がこの値を持つ場合はエラーになります。列挙型フィールドをこの値に設定しようとすると、ランタイムエラーが発生します。同様に、型付きアクセサを使用して、列挙型タイプで定義されていない列挙子に列挙型フィールドを設定しようとすると、ランタイムエラーが発生します。どちらのエラーの場合も、デバッグビルドではアサートが発生し、リリースビルドではログが記録され、フィールドがデフォルト値 (0
) に設定されます。
生の値アクセサは、ほとんどの場合使用されないため、Objective-C メソッドではなく C 関数として定義されています。C 関数として宣言すると、無駄な Objective-C ランタイム情報が削減され、リンカーがそれらをデッドストリップできる可能性があります。
Swift 列挙型サポート
Apple は、Objective-C 列挙型を Swift 列挙型にインポートする方法を C API との相互作用 でドキュメント化しています。プロトコルバッファで生成された列挙型は、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 types (proto3 のみ)
proto3 で提供されるメッセージ型を使用する場合、一般的に生成された Objective-C コードでは proto 定義がそのまま使用されますが、それらをより簡単に使用できるようにするために、カテゴリにいくつかの基本的な変換メソッドを提供しています。Any
(Any
のメッセージ値を適切な型のメッセージに変換するヘルパーメソッドは現在ありません) を含む、すべての Well-known types 用の特別な API はまだ用意されていないことに注意してください。
タイムスタンプ
@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 {
optional int32 foo = 101;
repeated int32 repeated_foo = 102;
}
message Bar {
extend Foo {
optional 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]]);