Objective-C Generierter Code Leitfaden
Alle Unterschiede zwischen generiertem Code für proto2, proto3 und Editionen werden hervorgehoben. Sie sollten den Proto2-Sprachleitfaden und/oder Proto3-Sprachleitfaden und/oder den Editionsleitfaden lesen, bevor Sie dieses Dokument lesen.
Compiler-Aufruf
Der Protocol Buffer-Compiler erzeugt Objective-C-Ausgabe, wenn er mit dem Befehlszeilenflag --objc_out= aufgerufen wird. Der Parameter für die Option --objc_out= ist das Verzeichnis, in das der Compiler Ihre Objective-C-Ausgabe schreiben soll. Der Compiler erstellt für jede eingegebene .proto-Datei eine Header-Datei und eine Implementierungsdatei. Die Namen der Ausgabedateien werden berechnet, indem der Name der .proto-Datei genommen und die folgenden Änderungen vorgenommen werden:
- Der Dateiname wird ermittelt, indem der Basisname der
.proto-Datei in Camel Case umgewandelt wird. Zum Beispiel wird ausfoo_bar.protoFooBar. - Die Erweiterung (
.proto) wird durch entwederpbobjc.hoderpbobjc.mfür die Header- bzw. Implementierungsdatei ersetzt. - Der Proto-Pfad (angegeben mit dem Befehlszeilenflag
--proto_path=oder-I) wird durch den Ausgabepfad (angegeben mit dem Flag--objc_out=) ersetzt.
Wenn Sie also beispielsweise den Compiler wie folgt aufrufen:
protoc --proto_path=src --objc_out=build/gen src/foo.proto src/bar/baz.proto
liest der Compiler die Dateien src/foo.proto und src/bar/baz.proto und erzeugt vier Ausgabedateien: build/gen/Foo.pbobjc.h, build/gen/Foo.pbobjc.m, build/gen/bar/Baz.pbobjc.h und build/gen/bar/Baz.pbobjc.m. Der Compiler erstellt automatisch das Verzeichnis build/gen/bar, falls erforderlich, aber er erstellt nicht build oder build/gen; diese müssen bereits vorhanden sein.
Pakete (Packages)
Der von Protocol Buffer-Compiler generierte Objective-C-Code wird vom im .proto-Datei definierten Paketnamen nicht beeinflusst, da Objective-C keine sprachlich erzwungenen Namensräume hat. Stattdessen werden Objective-C-Klassennamen durch Präfixe unterschieden, über die Sie im nächsten Abschnitt mehr erfahren.
Klassenpräfix
Unter Berücksichtigung der folgenden Dateioption
option objc_class_prefix = "CGOOP";
Der angegebene String – in diesem Fall CGOOP – wird vor allen für diese .proto-Datei generierten Objective-C-Klassen gestellt. Verwenden Sie Präfixe mit 3 oder mehr Zeichen, wie von Apple empfohlen. Beachten Sie, dass alle 2-Buchstaben-Präfixe von Apple reserviert sind.
Camel-Case-Konvertierung
Idiomatisches Objective-C verwendet für alle Bezeichner Camel Case.
Nachrichten werden nicht umbenannt, da der Standard für Proto-Dateien darin besteht, Nachrichten bereits in Camel Case zu benennen. Es wird davon ausgegangen, dass der Benutzer die Konvention aus gutem Grund umgangen hat und die Implementierung ihren Absichten entspricht.
Methoden, die aus Feldnamen und oneofs, enum-Deklarationen und Erweiterungsaccessoren generiert werden, werden in Camel Case umbenannt. Im Allgemeinen, um einen Proto-Namen in einen Objective-C-Namen in Camel Case umzuwandeln:
- Der erste Buchstabe wird großgeschrieben (außer bei Feldern, die immer mit einem Kleinbuchstaben beginnen).
- Für jeden Unterstrich im Namen wird der Unterstrich entfernt und der folgende Buchstabe großgeschrieben.
Aus foo_bar_baz wird also beispielsweise fooBarBaz. Aus FOO_bar wird fooBar.
Nachrichten
Angesichts einer einfachen Nachrichten Deklaration:
message Foo {}
Der Protocol Buffer-Compiler generiert eine Klasse namens Foo. Wenn Sie eine objc_class_prefix Dateioption angeben, wird der Wert dieser Option dem generierten Klassennamen vorangestellt.
Im Falle von äußeren Nachrichten, deren Namen mit C/C++- oder Objective-C-Schlüsselwörtern übereinstimmen,
message static {}
werden die generierten Interfaces wie folgt mit _Class ergänzt:
@interface static_Class {}
Beachten Sie, dass gemäß den Camel-Case-Konvertierungsregeln der Name static *nicht* konvertiert wird. Im Falle einer inneren Nachricht mit einem Camel-Case-Namen, der FieldNumber oder OneOfCase ist, wird das generierte Interface der Camel-Case-Name sein, ergänzt um _Class, um sicherzustellen, dass die generierten Namen nicht mit den FieldNumber-Enumerationen oder OneOfCase-Enumerationen kollidieren.
Eine Nachricht kann auch innerhalb einer anderen Nachricht deklariert werden.
message Foo {
message Bar {}
}
Dies generiert
@interface Foo_Bar : GPBMessage
@end
Wie Sie sehen, ist der generierte Name für verschachtelte Nachrichten der Name der generierten enthaltenden Nachricht (Foo), angehängt mit einem Unterstrich (_) und dem Namen der verschachtelten Nachricht (Bar).
Hinweis: Obwohl wir versucht haben, Konflikte zu minimieren, gibt es immer noch potenzielle Fälle, in denen Nachrichtennamen aufgrund der Konvertierung zwischen Unterstrichen und Camel Case kollidieren können. Als Beispiel:
message foo_bar {} message foo { message bar {} }generieren beide
@interface foo_barund kollidieren. Die pragmatischste Lösung mag darin bestehen, die kollidierenden Nachrichten umzubenennen.
GPBMessage-Interface
GPBMessage ist die Superklasse aller generierten Nachricht-Klassen. Sie ist erforderlich, um eine Teilmenge der folgenden Schnittstelle zu unterstützen:
@interface GPBMessage : NSObject
@end
Die Verhaltensweisen für diese Schnittstelle sind wie folgt:
// Will do a deep copy.
- (id)copy;
// Will perform a deep equality comparison.
- (BOOL)isEqual:(id)value;
Unbekannte Felder
Wenn eine Nachricht analysiert wird, kann sie Felder enthalten, die dem Parsing-Code nicht bekannt sind. Dies kann passieren, wenn eine Nachricht mit einer älteren Version einer .proto-Definition erstellt und dann mit Code analysiert wird, der aus einer neueren Version generiert wurde (oder umgekehrt).
Diese Felder werden nicht verworfen und in der unknownFields-Eigenschaft der Nachricht gespeichert.
@property(nonatomic, copy, nullable) GPBUnknownFieldSet *unknownFields;
Sie können die GPBUnknownFieldSet-Schnittstelle verwenden, um diese Felder anhand der Nummer abzurufen oder sie als Array zu durchlaufen.
Felder
Die folgenden Abschnitte beschreiben den vom Protocol Buffer-Compiler für Nachrichtenfelder generierten Code. Sie sind unterteilt in solche mit impliziter und expliziter Anwesenheit. Weitere Informationen zu dieser Unterscheidung finden Sie unter Field Presence.
Singuläre Felder mit impliziter Anwesenheit
Für jedes singuläre Feld generiert der Compiler eine Eigenschaft zur Speicherung von Daten und eine ganzzahlige Konstante, die die Feldnummer enthält. Felder vom Nachrichtentyp erhalten außerdem eine has..-Eigenschaft, mit der Sie überprüfen können, ob das Feld in der kodierten Nachricht gesetzt ist. Angenommen, Sie haben also die folgende Nachricht:
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;
}
Generiert der Compiler Folgendes:
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
Spezielle Namensfälle
Es gibt Fälle, in denen die Regeln zur Generierung von Feldnamen zu Namenskonflikten führen können und Namen "eindeutig" gemacht werden müssen. Solche Konflikte werden gelöst, indem _p an das Ende des Feldes angehängt wird (_p wurde gewählt, weil es ziemlich eindeutig ist und für "property" steht).
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
}
generiert
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
Standardwerte
Der Standardwert für numerische Typen ist 0.
Der Standardwert für Strings ist @"" und der Standardwert für Bytes ist [NSData data].
Das Zuweisen von nil zu einem String-Feld führt in Debug-Builds zu einem Assertion-Fehler und setzt das Feld in Release-Builds auf @"". Das Zuweisen von nil zu einem Bytes-Feld führt in Debug-Builds zu einem Assertion-Fehler und setzt das Feld in Release-Builds auf [NSData data]. Um zu prüfen, ob ein Bytes- oder String-Feld gesetzt ist, muss seine Längen-Eigenschaft getestet und mit 0 verglichen werden.
Der Standardwert für eine Nachricht ist eine Instanz der Standardnachricht. Um einen Nachrichtenwert zu löschen, sollte er auf nil gesetzt werden. Der Zugriff auf eine gelöschte Nachricht gibt eine Instanz der Standardnachricht zurück und die Methode hasFoo gibt false zurück.
Die für ein Feld zurückgegebene Standardnachricht ist eine lokale Instanz. Der Grund für die Rückgabe einer Standardnachricht anstelle von nil ist, dass im Falle von
message Foo {
message Bar {
int32 b;
}
Bar a;
}
Die Implementierung wird unterstützen
Foo *foo = [[Foo alloc] init];
foo.a.b = 2;
wobei a bei Bedarf automatisch über die Accessoren erstellt wird. Wenn foo.a nil zurückgeben würde, würde das Setter-Muster foo.a.b nicht funktionieren.
Singuläre Felder mit expliziter Anwesenheit
Für jedes singuläre Feld generiert der Compiler eine Eigenschaft zur Speicherung von Daten, eine ganzzahlige Konstante mit der Feldnummer und eine has..-Eigenschaft, mit der Sie überprüfen können, ob das Feld in der kodierten Nachricht gesetzt ist. Angenommen, Sie haben also die folgende Nachricht:
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;
}
Generiert der Compiler Folgendes:
# 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
Spezielle Namensfälle
Es gibt Fälle, in denen die Regeln zur Generierung von Feldnamen zu Namenskonflikten führen können und Namen "eindeutig" gemacht werden müssen. Solche Konflikte werden gelöst, indem _p an das Ende des Feldes angehängt wird (_p wurde gewählt, weil es ziemlich eindeutig ist und für "property" steht).
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
}
generiert
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
Standardwerte (nur optionale Felder)
Der Standardwert für numerische Typen ist 0, sofern kein expliziter Standardwert vom Benutzer angegeben wurde.
Der Standardwert für Strings ist @"" und der Standardwert für Bytes ist [NSData data].
Das Zuweisen von nil zu einem String-Feld führt in Debug-Builds zu einem Assertion-Fehler und setzt das Feld in Release-Builds auf @"". Das Zuweisen von nil zu einem Bytes-Feld führt in Debug-Builds zu einem Assertion-Fehler und setzt das Feld in Release-Builds auf [NSData data]. Um zu prüfen, ob ein Bytes- oder String-Feld gesetzt ist, muss seine Längen-Eigenschaft getestet und mit 0 verglichen werden.
Der Standardwert für eine Nachricht ist eine Instanz der Standardnachricht. Um einen Nachrichtenwert zu löschen, sollte er auf nil gesetzt werden. Der Zugriff auf eine gelöschte Nachricht gibt eine Instanz der Standardnachricht zurück und die Methode hasFoo gibt false zurück.
Die für ein Feld zurückgegebene Standardnachricht ist eine lokale Instanz. Der Grund für die Rückgabe einer Standardnachricht anstelle von nil ist, dass im Falle von
message Foo {
message Bar {
int32 b;
}
Bar a;
}
Die Implementierung wird unterstützen
Foo *foo = [[Foo alloc] init];
foo.a.b = 2;
wobei a bei Bedarf automatisch über die Accessoren erstellt wird. Wenn foo.a nil zurückgeben würde, würde das Setter-Muster foo.a.b nicht funktionieren.
Wiederholte Felder
Wie bei singulären Feldern (proto2 proto3) generiert der Protocol Buffer-Compiler eine Daten-Eigenschaft für jedes wiederholte Feld. Diese Daten-Eigenschaft ist ein GPB<VALUE>Array, abhängig vom Feldtyp, wobei <VALUE> eines von UInt32, Int32, UInt64, Int64, Bool, Float, Double oder Enum sein kann. NSMutableArray wird für string, bytes und message-Typen verwendet. Feldnamen für wiederholte Typen haben Array angehängt. Der Grund für das Anhängen von Array an die Objective-C-Schnittstelle ist, den Code lesbarer zu machen. Wiederholte Felder in Proto-Dateien haben oft singuläre Namen, die im Standard-Objective-C-Gebrauch nicht gut klingen. Die Singularnamen in Plural zu ändern wäre idiomatischer für Objective-C, aber Pluralisierungsregeln sind zu komplex, um sie im Compiler zu unterstützen.
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;
}
generiert
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
Hinweis: Das Verhalten von wiederholten Feldern kann in Editionen mit dem features.repeated_field_encoding Feature konfiguriert werden.
Für String-, Bytes- und Nachrichtenfelder sind die Elemente des Arrays NSString*, NSData* und Zeiger auf Unterklassen von GPBMessage.
Standardwerte
Der Standardwert für ein wiederholtes Feld ist leer. Im Objective-C-generierten Code ist dies ein leeres GPB<VALUE>Array. Wenn Sie auf ein leeres wiederholtes Feld zugreifen, erhalten Sie ein leeres Array, das Sie wie jedes andere wiederholte Feldarray aktualisieren können.
Foo *myFoo = [[Foo alloc] init];
[myFoo.stringValueArray addObject:@"A string"]
Sie können auch die bereitgestellte <field>Array_Count-Eigenschaft verwenden, um zu prüfen, ob das Array für ein bestimmtes wiederholtes Feld leer ist, ohne das Array erstellen zu müssen.
if (myFoo.messageValueArray_Count) {
// There is something in the array...
}
GPB<VALUE>Array-Schnittstelle
GPB<VALUE>Arrays (außer GPBEnumArray, das wir unten betrachten) haben die folgende Schnittstelle:
@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 hat eine etwas andere Schnittstelle, um die Validierungsfunktion zu handhaben und auf Rohwerte zuzugreifen.
@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-Felder
Gegeben eine Nachricht mit oneof-Felddefinitionen:
message Order {
oneof OrderID {
string name = 1;
int32 address = 2;
};
int32 quantity = 3;
};
Generiert der Protocol Buffer-Compiler:
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);
Das Setzen einer der Oneof-Eigenschaften löscht alle anderen Eigenschaften, die mit dem Oneof verbunden sind.
<ONE_OF_NAME>_OneOfCase_GPBUnsetOneOfCase ist immer äquivalent zu 0, um leicht testen zu können, ob ein Feld im Oneof gesetzt ist.
Map-Felder
Für diese Nachrichtendefinition:
message Bar {...}
message Foo {
map<int32, string> a_map = 1;
map<string, Bar> b_map = 2;
};
Generiert der Compiler Folgendes:
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
Fälle, in denen Schlüssel Strings und Werte Strings, Bytes oder Nachrichten sind, werden von NSMutableDictionary behandelt.
Andere Fälle sind:
GBP<KEY><VALUE>Dictionary
wobei
<KEY>ist Uint32, Int32, UInt64, Int64, Bool oder String.<VALUE>ist UInt32, Int32, UInt64, Int64, Bool, Float, Double, Enum oder Object.Objectwird für Werte vom Typstring,bytesodermessageverwendet, um die Anzahl der Klassen zu reduzieren und steht im Einklang damit, wie Objective-C mitNSMutableDictionaryarbeitet.
Standardwerte
Der Standardwert für ein Map-Feld ist leer. Im Objective-C-generierten Code ist dies ein leeres GBP<KEY><VALUE>Dictionary. Wenn Sie auf ein leeres Map-Feld zugreifen, erhalten Sie ein leeres Dictionary, das Sie wie jedes andere Map-Feld aktualisieren können.
Sie können auch die bereitgestellte <mapField>_Count-Eigenschaft verwenden, um zu prüfen, ob eine bestimmte Map leer ist.
if (myFoo.myMap_Count) {
// There is something in the map...
}
GBP<KEY><VALUE>Dictionary-Schnittstelle
Die Schnittstelle von GBP<KEY><VALUE>Dictionary (außer GBP<KEY>ObjectDictionary und GBP<KEY>EnumDictionary) ist wie folgt:
@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
Die Schnittstelle von GBP<KEY>ObjectDictionary ist:
@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 hat eine etwas andere Schnittstelle, um die Validierungsfunktion zu handhaben und auf Rohwerte zuzugreifen.
@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
Aufzählungen (Enums)
Gegeben eine Enum-Definition wie
enum Foo {
VALUE_A = 0;
VALUE_B = 1;
VALUE_C = 5;
}
wird der generierte Code sein:
// 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();
Jede Enumeration hat eine Validierungsfunktion dafür deklariert
// Returns YES if the given numeric value matches one of Foo's
// defined values (0, 1, 5).
BOOL Foo_IsValidValue(int32_t value);
und eine Enum-Deskriptor-Accessor-Funktion dafür deklariert
// 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)();
Die Enum-Deskriptor-Accessor-Funktionen sind C-Funktionen und keine Methoden auf der Enum-Klasse, da sie von Client-Software selten verwendet werden. Dies reduziert die Menge der generierten Objective-C-Laufzeitinformationen und ermöglicht es dem Linker möglicherweise, sie totzuschneiden.
Im Falle von äußeren Enums, deren Namen mit C/C++- oder Objective-C-Schlüsselwörtern übereinstimmen, wie z. B.:
enum method {}
werden die generierten Interfaces mit _Enum ergänzt, wie folgt:
// The generated enumeration name is the keyword suffixed by _Enum.
typedef GPB_ENUM(Method_Enum) {}
Eine Enum kann auch innerhalb einer anderen Nachricht deklariert werden. Zum Beispiel:
message Foo {
enum Bar {
VALUE_A = 0;
VALUE_B = 1;
VALUE_C = 5;
}
Bar aBar = 1;
Bar aDifferentBar = 2;
repeated Bar aRepeatedBar = 3;
}
generiert
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);
Alle Enum-Felder haben die Möglichkeit, auf den Wert als typisierten Enumerator (Foo_Bar im obigen Beispiel) zuzugreifen, oder, wenn proto3 verwendet wird, als rohen int32_t-Wert (mithilfe der Accessor-Funktionen im obigen Beispiel). Dies dient der Unterstützung des Falls, dass der Server Werte zurückgibt, die der Client möglicherweise nicht erkennt, da Client und Server mit unterschiedlichen Versionen der Proto-Datei kompiliert wurden.
Nicht erkannte Enum-Werte werden je nach Sprachversion und dem features.enum_type-Feature in Editionen unterschiedlich behandelt.
- In offenen Enums wird
kGPBUnrecognizedEnumeratorValuefür den typisierten Enumerator-Wert zurückgegeben, wenn der Enumerator-Wert in den analysierten Nachrichtendaten keiner ist, den der lesende Code unterstützt hat. Wenn der tatsächliche Wert gewünscht wird, verwenden Sie die Rohwert-Accessoren, um den Wert alsint32_tzu erhalten. - In geschlossenen Enums werden nicht erkannte Enum-Werte als unbekannte Felder behandelt.
- Proto2-Enums sind geschlossen, und Proto3-Enums sind offen. In Editionen ist das Verhalten mit dem
features.enum_type-Feature konfigurierbar.
kGPBUnrecognizedEnumeratorValue ist definiert als 0xFBADBEEF, und es wird ein Fehler sein, wenn ein Enumerator in einer Enumeration diesen Wert hat. Der Versuch, ein Enum-Feld auf diesen Wert zu setzen, ist ein Laufzeitfehler. Ebenso ist der Versuch, ein Enum-Feld mithilfe der typisierten Accessoren auf einen nicht durch seinen Enum-Typ definierten Enumerator zu setzen, ein Laufzeitfehler. In beiden Fehlerfällen wird in Debug-Builds eine Assertion ausgelöst und in Release-Builds wird das Feld protokolliert und auf seinen Standardwert (0) gesetzt.
Die Rohwert-Accessoren sind als C-Funktionen und nicht als Objective-C-Methoden definiert, da sie in den meisten Fällen nicht verwendet werden. Die Deklaration als C-Funktionen reduziert unnötige Objective-C-Laufzeitinformationen und ermöglicht es dem Linker, sie potenziell totzuschneiden.
Swift-Enumerationsunterstützung
Apple dokumentiert, wie sie Objective-C-Enumerationen in Swift-Enumerationen importieren, in Interacting with C APIs. Von Protocol Buffers generierte Enumerationen unterstützen Objective-C-zu-Swift-Konvertierungen.
// Proto
enum Foo {
VALUE_A = 0;
}
generiert
// Objective-C
typedef GPB_ENUM(Foo) {
Foo_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
Foo_ValueA = 0,
};
was im Swift-Code Folgendes ermöglicht:
// Swift
let aValue = Foo.ValueA
let anotherValue: Foo = .GPBUnrecognizedEnumeratorValue
Gut bekannte Typen
Wenn Sie eine der mit Protocol Buffers bereitgestellten Nachrichtentypen verwenden, werden diese im Allgemeinen einfach ihre Proto-Definitionen in generiertem Objective-C-Code verwenden, obwohl wir einige grundlegende Konvertierungsmethoden in Kategorien bereitstellen, um ihre Verwendung zu vereinfachen. Beachten Sie, dass wir noch keine speziellen APIs für alle gut bekannten Typen haben, einschließlich Any (derzeit gibt es keine Hilfsmethode, um den Nachrichtenwert eines Any in eine Nachricht des entsprechenden Typs zu konvertieren).
Zeitstempel
@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
Erweiterungen (nur proto2 und Editionen)
Gegeben eine Nachricht mit einer Erweiterungsspanne:
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;
}
}
Generiert der Compiler Folgendes:
# 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
Um diese Erweiterungsfelder abzurufen und zu setzen, verwenden Sie Folgendes:
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]]);