REMARQUE:Ce site est obsolète. Le site sera désactivé après le 31 janvier 2023 et le trafic redirigera vers le nouveau site à l'adresse https://protobuf.dev. En attendant, les mises à jour ne seront effectuées que sur protobuf.dev.

Code généré par Objective-C

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Cette page décrit le code Objective-C généré par le compilateur de tampon de protocole pour une définition de protocole donnée. Toute différence entre le code généré par proto2 et le code proto3 est mis en évidence. Lisez le guide du langage proto2 et/ou le guide du langage proto3 avant de lire ce document.

Appel du compilateur

Le compilateur de tampon de protocole génère une sortie Objective-C lorsqu'elle est appelée avec l'option de ligne de commande --objc_out=. Le paramètre de l'option --objc_out= correspond au répertoire dans lequel vous souhaitez que le compilateur écrive votre sortie Objective-C. Le compilateur crée un fichier d'en-tête et un fichier d'implémentation pour chaque entrée de fichier .proto. Les noms des fichiers de sortie sont calculés en prenant le nom du fichier .proto et en apportant les modifications suivantes:

  • Le nom de fichier est déterminé en convertissant le nom de base du fichier .proto en casse mixte. Par exemple, foo_bar.proto deviendra FooBar.
  • L'extension (.proto) est remplacée par pbobjc.h ou pbobjc.m pour l'en-tête ou le fichier d'implémentation, respectivement.
  • Le chemin d'accès proto (spécifié avec l'option de ligne de commande --proto_path= ou -I) est remplacé par le chemin de sortie (spécifié avec l'option --objc_out=).

Par exemple, si vous appelez le compilateur comme suit :

protoc --proto_path=src --objc_out=build/gen src/foo.proto src/bar/baz.proto

Le compilateur lit les fichiers src/foo.proto et src/bar/baz.proto, puis produit quatre fichiers de sortie : build/gen/Foo.pbobjc.h, build/gen/Foo.pbobjc.m, build/gen/bar/Baz.pbobjc.h et build/gen/bar/Baz.pbobjc.m. Le compilateur crée automatiquement le répertoire build/gen/bar si nécessaire, mais il ne crée pas build ni build/gen. Ils doivent déjà exister.

Colis

Le code Objective-C généré par le compilateur de tampon de protocole n'est pas affecté par le nom de package défini dans le fichier .proto, car Objective-C n'a pas d'espace de noms formé par le langage. À la place, les noms de classe Objective-C sont distingués à l'aide de préfixes, que vous découvrirez dans la section suivante.

Préfixe de la classe

Compte tenu de l'option de fichier suivante :

option objc_class_prefix = "CGOOP";

La chaîne spécifiée (dans ce cas, CGOOP) est précédée de toutes les classes Objective-C générées pour ce fichier .proto. Veuillez utiliser des préfixes d'au moins trois caractères, conformément aux recommandations d'Apple. Notez que les deux préfixes de lettres sont réservés par Apple.

Conversion de chameaux

Objective-C utilise le cas de chameau pour tous les identifiants.

Le nom des messages ne sera pas converti, car la norme pour les fichiers proto consiste déjà à nommer les messages en casse mixte. Nous partons du principe que l'utilisateur a ignoré la convention pour une bonne raison, et que la mise en œuvre sera conforme à ses intentions.

Le chameau des méthodes générées à partir des noms de champs et des oneof, des déclarations enum et des accesseurs d'extensions sera utilisé. En général, pour passer d'un nom proto à un nom Objective-C en chameau, procédez comme suit:

  • La première lettre convertie en majuscules (sauf pour les champs, qui commencent toujours par une lettre minuscule).
  • Chaque trait de soulignement dans le nom est supprimé, et la lettre suivante est en majuscule.

Par exemple, le champ foo_bar_baz devient fooBarBaz. Le champ FOO_bar devient fooBar.

Messages

Voici une déclaration de message simple :

message Foo {}

Le compilateur de tampon de protocole génère une classe appelée Foo. Si vous spécifiez une option de fichier objc_class_prefix, la valeur de cette option est ajoutée au début du nom de classe généré.

Dans le cas de messages externes dont le nom correspond à l'un des mots clés C/C++ ou Objective-C :

message static {}

les interfaces générées sont annotées avec le suffixe _Class, comme suit :

@interface static_Class {}

Notez que, conformément aux Règles de conversion de casse en chameau, le nom static n'est pas converti. Dans le cas d'un message interne dont le nom en chameau est FieldNumber ou OneOfCase, l'interface générée correspond au nom en forme de chameau, suffixeé par _Class, afin de garantir que les noms générés n'entrent pas en conflit avec les énumérations FieldNumber ou OneOfCase.

Un message peut également être déclaré dans un autre message.

message Foo {
  message Bar {}
}

Vous générez ainsi :

@interface Foo_Bar : GPBMessage
@end

Comme vous pouvez le voir, le nom du message imbriqué généré est celui qui contient le message généré (Foo) avec un trait de soulignement (_) et le nom du message imbriqué (Bar).

Nous avons tout mis en œuvre pour éviter autant que possible les conflits, mais il peut arriver que des noms de messages entrent en conflit en raison de la conversion entre les traits de soulignement et le camel case. Par exemple,

message foo_bar {}
message foo { message bar {} }
va générer à la fois @interface foo_bar et créer un conflit. La solution la plus pragmatique consiste peut-être à renommer les messages en conflit.

Interface GPBMessage

GPBMessage est la superclasse de toutes les classes de message générées. Elle doit être compatible avec un sur-ensemble de l'interface suivante :
@interface GPBMessage : NSObject 
@end

Cette interface présente les comportements suivants :

// Will do a deep copy.
- (id)copy;
// Will perform a deep equality comparison.
- (BOOL)isEqual:(id)value;

Champs inconnus (proto2 uniquement)

Si un message créé avec une ancienne version de votre définition .proto est analysé avec du code généré à partir d'une version plus récente (ou inversement), il peut contenir des champs facultatifs ou répétés que le "nouveau" code ne reconnaît pas. Dans le code généré par proto2, ces champs ne sont pas supprimés et sont stockés dans la propriété unknownFields du message.

@property(nonatomic, copy, nullable) GPBUnknownFieldSet *unknownFields;

Vous pouvez utiliser l'interface GPBUnknownFieldSet pour récupérer ces champs par numéro ou les boucler sur un tableau.

Dans proto3, les champs inconnus sont simplement ignorés lors de l'analyse d'un message.

Champs

Les sections suivantes décrivent le code généré par le compilateur de tampon de protocole pour les champs de message.

Champs singuliers (proto3)

Pour chaque champ unique, le compilateur génère une propriété pour stocker des données et une constante entière contenant le numéro de champ. Les champs de type "Message" affichent également une propriété has.. qui vous permet de vérifier si le champ est défini dans le message encodé. Par exemple, avec le message suivant :

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;
}

Le compilateur générera les éléments suivants :

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

Cas particuliers de dénomination

Dans certains cas, les règles de génération de noms de champ peuvent entraîner des conflits de noms, et les noms doivent être "uniques". Ces conflits sont résolus en ajoutant _p à la fin du champ (_p a été sélectionné, car il est assez unique et signifie "propriété").

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
}

génère :

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

Valeurs par défaut

La valeur par défaut pour les types numériques est 0.

La valeur par défaut pour les chaînes est @"", et la valeur par défaut pour les octets est [NSData data].

L'attribution de nil à un champ de chaîne sera déclarée lors du débogage et définira le champ sur @"" dans la version. L'attribution de nil à un champ d'octets sera déclarée lors du débogage et définira le champ sur [NSData data] dans la version. Pour déterminer si un champ d'octets ou de chaîne est défini, vous devez tester sa propriété "length" et la comparer à 0.

La valeur "empty" (vide) par défaut d'un message est une instance du message par défaut. Pour effacer la valeur d'un message, définissez-la sur nil. L'accès à un message effacé renvoie une instance du message par défaut, et la méthode hasFoo renvoie la valeur "false".

Le message par défaut renvoyé pour un champ est une instance locale. La raison pour laquelle un message par défaut est renvoyé au lieu de nil est la suivante :

message Foo {
  message Bar {
     int32 b;
  }
  Bar a;
}

L'implémentation sera compatible avec :

Foo *foo = [[Foo alloc] init];
foo.a.b = 2;

a sera automatiquement créé via les accesseurs si nécessaire. Si foo.a renvoie nil, le modèle setter foo.a.b ne fonctionnera pas.

Champs singuliers (proto2)

Pour chaque champ unique, le compilateur génère une propriété pour stocker des données, une constante entière contenant le numéro du champ et une propriété has.. qui vous permet de vérifier si le champ est défini dans le message encodé. Par exemple, avec le message suivant :

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;
}

Le compilateur générera les éléments suivants :

# 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

Cas particuliers de dénomination

Dans certains cas, les règles de génération de noms de champ peuvent entraîner des conflits de noms, et les noms doivent être "uniques". Ces conflits sont résolus en ajoutant _p à la fin du champ (_p a été sélectionné, car il est assez unique et signifie "propriété").

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
}

génère :

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

Valeurs par défaut (champs facultatifs uniquement)

La valeur par défaut pour les types numériques, si aucune valeur par défaut explicite n'a été spécifiée par l'utilisateur, est 0.

La valeur par défaut pour les chaînes est @"", et la valeur par défaut pour les octets est [NSData data].

L'attribution de nil à un champ de chaîne sera déclarée lors du débogage et définira le champ sur @"" dans la version. L'attribution de nil à un champ d'octets sera déclarée lors du débogage et définira le champ sur [NSData data] dans la version. Pour déterminer si un champ d'octets ou de chaîne est défini, vous devez tester sa propriété "length" et la comparer à 0.

La valeur "empty" (vide) par défaut d'un message est une instance du message par défaut. Pour effacer la valeur d'un message, définissez-la sur nil. L'accès à un message effacé renvoie une instance du message par défaut, et la méthode hasFoo renvoie la valeur "false".

Le message par défaut renvoyé pour un champ est une instance locale. La raison pour laquelle un message par défaut est renvoyé au lieu de nil est la suivante :

message Foo {
  message Bar {
     int32 b;
  }
  Bar a;
}

L'implémentation sera compatible avec :

Foo *foo = [[Foo alloc] init];
foo.a.b = 2;

a sera automatiquement créé via les accesseurs si nécessaire. Si foo.a renvoie nil, le modèle setter foo.a.b ne fonctionnera pas.

Champs répétés

Comme les champs individuels (proto2proto3), le compilateur de tampon de protocole génère une propriété de données pour chaque champ répété. Cette propriété de données est un GPB<VALUE>Array selon le type de champ où <VALUE> peut être UInt32, Int32, UInt64, Int64, Bool, Float, Double ou Enum. NSMutableArray sera utilisé pour les types string, bytes et message. Le nom des champs répétés pour les types répétés est suivi d'un caractère Array. L'ajout de Array dans l'interface Objective-C permet de rendre le code plus lisible. Dans les fichiers proto, les champs répétés ont généralement des noms uniques, qui ne sont pas bien lisibles dans l'utilisation standard d'Objective-C. Rendre les noms au singulier au pluriel serait plus idiomatique avec Objective-C, mais les règles de pluralisation sont trop complexes pour être compatibles avec le compilateur.

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;
}

génère :

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

Pour les champs de type chaîne, octets et messages, les éléments du tableau sont respectivement NSString* et NSData*, et pointent vers les sous-classes de GPBMessage.

Valeurs par défaut

La valeur par défaut d'un champ répété doit être vide. Dans le code généré par Objective-C, il s'agit d'un GPB<VALUE>Array vide. Si vous accédez à un champ répété vide, vous obtenez un tableau vide que vous pouvez mettre à jour comme n'importe quel autre tableau de champs répétés.

Foo *myFoo = [[Foo alloc] init];
[myFoo.stringValueArray addObject:@"A string"]

Vous pouvez également utiliser la propriété <field>Array_Count fournie pour vérifier si le tableau d'un champ répété spécifique est vide sans avoir à créer le tableau :

if (myFoo.messageValueArray_Count) {
  // There is something in the array...
}

Interface GPB<VALUE>Array

GPB<VALUE>Array (à l'exception de GPBEnumArray, que nous allons voir ci-dessous) ont l'interface suivante :

@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 dispose d'une interface légèrement différente pour gérer la fonction de validation et accéder aux valeurs brutes.

@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

Un des champs

Message présenté avec l'un des définitions de champs :

message Order {
  oneof OrderID {
    string name = 1;
    int32 address = 2;
  };
  int32 quantity = 3;
};

Le compilateur de tampon de protocole génère :

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);

Si vous définissez l'une des propriétés, toutes les autres propriétés seront supprimées.

<ONE_OF_NAME>_OneOfCase_GPBUnsetOneOfCase sera toujours équivalent à 0 pour faciliter les tests afin de déterminer si l'un des champs est défini.

Champs de carte

Pour cette définition de message :

message Bar {...}
message Foo {
  map<int32, string> a_map = 1;
  map<string, Bar> b_map = 2;
};

Le compilateur génère les éléments suivants :

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

Les clés sont des chaînes et des valeurs des chaînes, des octets ou des messages gérés par NSMutableDictionary.

Voici d'autres cas :

GBP<KEY><VALUE>Dictionary

où :

  • <KEY> est Uint32, Int32, UInt64, Int64, Bool ou String.
  • <VALUE> est UInt32, Int32, UInt64, Int64, Bool, Float, Double, Enum ou Object. Object est utilisé pour les valeurs de type string bytes ou message afin de réduire le nombre de classes. Il est conforme au fonctionnement d'Objective-C avec NSMutableDictionary.

Valeurs par défaut

La valeur par défaut d'un champ de mappage est vide. Dans le code généré par Objective-C, il s'agit d'un GBP<KEY><VALUE>Dictionary vide. Si vous accédez à un champ de carte vide, vous obtiendrez un dictionnaire vide que vous pourrez mettre à jour comme n'importe quel autre champ de carte.

Vous pouvez également utiliser la propriété <mapField$gt;_Count fournie pour vérifier si une carte particulière est vide :

if (myFoo.myMap_Count) {
  // There is something in the map...
}

Interface GBP<KEY><VALUE>Dictionary

L'interface GBP<KEY><VALUE>Dictionary (à l'exception de GBP<KEY>ObjectDictionary et GBP<KEY>EnumDictionary) est la suivante :

@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

L'interface GBP<KEY>ObjectDictionary est la suivante :

@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 dispose d'une interface légèrement différente pour gérer la fonction de validation et accéder aux valeurs brutes.

@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

Énumérations

Selon une définition d'énumération comme :

enum Foo {
  VALUE_A = 0;
  VALUE_B = 1;
  VALUE_C = 5;
}

le code généré sera le suivant :

// 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();

Chaque énumération possède une fonction de validation déclarée :


// Returns YES if the given numeric value matches one of Foo's
// defined values (0, 1, 5).
BOOL Foo_IsValidValue(int32_t value);

et d'une fonction d'accesseur de descripteur d'énumération déclarée pour celle-ci :

// 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)();

Les fonctions d'accesseur au descripteur d'énumération sont des fonctions C, et non des méthodes de la classe d'énumération, car elles sont rarement utilisées par les logiciels clients. Cela permettra de réduire la quantité d'informations d'exécution Objective-C générées, et peut éventuellement permettre à l'outil d'association de les ignorer.

Dans le cas des énumérations externes dont les noms correspondent à des mots clés C/C++ ou Objective-C, par exemple :

enum method {}

Les interfaces générées portent le suffixe _Enum, comme suit :

// The generated enumeration name is the keyword suffixed by _Enum.
typedef GPB_ENUM(Method_Enum) {}

Une énumération peut également être déclarée dans un autre message. Exemple :

message Foo {
  enum Bar {
    VALUE_A = 0;
    VALUE_B = 1;
    VALUE_C = 5;
  }
  Bar aBar = 1;
  Bar aDifferentBar = 2;
  repeated Bar aRepeatedBar = 3;
}

génère :

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);

Tous les champs d'énumération peuvent accéder à la valeur en tant qu'énumération saisie (Foo_Bar dans l'exemple ci-dessus) ou, si vous utilisez proto3, en tant que valeur int32_t brute (à l'aide des fonctions d'accesseur dans l'exemple ci-dessus). Cela convient au cas où le serveur renvoie des valeurs que le client peut ne pas reconnaître en raison de la compilation du client et du serveur avec différentes versions du fichier proto.

Les valeurs d'énumération non reconnues sont traitées différemment selon la version du tampon de protocole que vous utilisez. Dans proto3, kGPBUnrecognizedEnumeratorValue est renvoyé pour la valeur d'énumération saisie si la valeur d'énumérateur dans les données de message analysées n'est pas celle que le code l'ayant compilé a été compilée. Si la valeur réelle est souhaitée, utilisez les accesseurs de valeurs brutes pour obtenir la valeur en tant que int32_t. Si vous utilisez proto2, les valeurs d'énumération non reconnues sont traitées comme des champs inconnus.

kGPBUnrecognizedEnumeratorValue est défini comme 0xFBADBEEF, et il s'agira d'une erreur si un énumérateur dans une énumération a cette valeur. Toute tentative de définition d'un champ d'énumération sur cette valeur constitue une erreur d'exécution. De même, toute tentative de définir un champ d'énumération sur un énumérateur non défini par son type d'énumération à l'aide des accesseurs saisis est une erreur d'exécution. Dans les deux cas d'erreur, les compilations de débogage engendreront une assertion, et les versions de version consigneront et définiront la valeur par défaut du champ (0).

Les accesseurs de valeurs brutes sont définis comme des fonctions C plutôt que comme des méthodes Objective-C, car ils ne sont pas utilisés dans la plupart des cas. Les déclarer en tant que fonctions C réduisent le gaspillage des informations d'exécution d'Objective-C et permettent au linker de les supprimer.

Assistance pour l'énumération Swift

Apple explique comment importer des énumérations Objective-C vers des énumérations Swift dans la section Interagir avec les API C. Les énumérations générées par le tampon de protocole sont compatibles avec les conversions Objective-C vers Swift.
// Proto
enum Foo {
  VALUE_A = 0;
}

génère :

// Objective-C
typedef GPB_ENUM(Foo) {
  Foo_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue,
  Foo_ValueA = 0,
};

ce qui, dans Swift Code, permettra :

// Swift
let aValue = Foo.ValueA
let anotherValue: Foo = .GPBUnrecognizedEnumeratorValue

Types connus (proto3 uniquement)

Si vous utilisez l'un des types de messages fournis avec proto3, ils utilisent généralement leurs définitions proto dans le code Objective-C généré. Toutefois, pour plus de simplicité, nous fournissons des méthodes de conversion de base dans les catégories. Notez que nous ne disposons pas encore d'API spéciales pour tous les types connus, y compris Any (il n'existe actuellement aucune méthode d'assistance pour convertir la valeur d'un message Any en message du type approprié).

Horodatages

@interface GPBTimeStamp (GPBWellKnownTypes)
@property (nonatomic, readwrite, strong) NSDate *date;
@property (nonatomic, readwrite) NSTimeInterval timeIntervalSince1970;
- (instancetype)initWithDate:(NSDate *)date;
- (instancetype)initWithTimeIntervalSince1970:
    (NSTimeInterval)timeIntervalSince1970;
@end

Durée

@interface GPBDuration (GPBWellKnownTypes)
@property (nonatomic, readwrite) NSTimeInterval timeIntervalSince1970;
- (instancetype)initWithTimeIntervalSince1970:
    (NSTimeInterval)timeIntervalSince1970;
@end

Extensions (proto2 uniquement)

Message reçu avec une plage d'extensions :

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;
  }
}

Le compilateur génère les éléments suivants :


# 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

Pour obtenir et définir ces champs d'extension, utilisez les éléments suivants :

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]]);