Objective-C 生成コードガイド
proto2、proto3、Editions の生成コード間の違いが強調されています。このドキュメントを読む前に、proto2 言語ガイド、proto3 言語ガイド、および/または Editions ガイドを読む必要があります。
コンパイラの呼び出し
プロトコルバッファコンパイラは、--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 コードは、.proto
ファイルで定義されているパッケージ名にまったく影響されません。Objective-C には言語によって強制される名前空間がないためです。代わりに、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]
に設定されます。バイトまたは文字列フィールドが設定されているかどうかをテストするには、その 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
セッターパターンは機能しません。
明示的なプレゼンスを持つ単数フィールド
すべての単数フィールドに対して、コンパイラはデータを格納するプロパティ、フィールド番号を含む整数定数、およびエンコードされたメッセージでフィールドが設定されているかどうかを確認できる 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
を付加する理由は、コードの可読性を高めるためです。プロトファイルの繰り返しフィールドは単数形名を持つ傾向があり、標準的な 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
注: 繰り返しフィールドの動作は、Editions の 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
は常に 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)();
enum記述子アクセサ関数は、クライアントソフトウェアによってめったに使用されないため、列挙クラスのメソッドとは異なり、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 は Interacting with C APIs で 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 types
プロトコルバッファで提供されるメッセージ型を使用する場合、通常、生成された Objective-C コードではプロト定義がそのまま使用されますが、使用を簡単にするためにカテゴリで基本的な変換メソッドを提供しています。ただし、Any
を含むすべての周知の型に対して特別な 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
期間
@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]]);