- Invocação do compilador
- Pacotes
- Prefixo da classe
- Conversão de maiúsculas e minúsculas
- Mensagens
- Campos
- Enumerações
- Tipos conhecidos (somente proto3)
- Extensões (somente proto2)
Esta página descreve exatamente o código Objective-C que o compilador do buffer de protocolo gera para qualquer definição de protocolo. Qualquer diferença entre o código gerado pelo proto2 e pelo proto3 está destacada. Leia o guia de idiomas do proto2 e/ou o guia do idioma do proto3 antes de ler este documento.
Invocação do compilador
O compilador de buffer de protocolo produz a saída do Objective-C quando invocado com a sinalização de linha de comando --objc_out=
. O parâmetro para a opção --objc_out=
é o diretório em que você quer que o compilador grave a saída do Objective-C. O compilador cria um arquivo principal e um arquivo de implementação para cada entrada de arquivo .proto
. Os nomes dos arquivos de saída são calculados com base no nome do arquivo .proto
e fazendo as seguintes alterações:
- O nome do arquivo é determinado pela conversão do nome de base do arquivo
.proto
em letras concatenadas. Por exemplo,foo_bar.proto
se tornaráFooBar
. - A extensão (
.proto
) é substituída porpbobjc.h
oupbobjc.m
para o arquivo de cabeçalho ou implementação, respectivamente. - O caminho do proto (especificado com a sinalização de linha de comando
--proto_path=
ou-I
) é substituído pelo caminho de saída (especificado com a sinalização--objc_out=
).
Por exemplo, se você invocar o compilador da seguinte forma:
protoc --proto_path=src --objc_out=build/gen src/foo.proto src/bar/baz.proto
O compilador lerá os arquivos src/foo.proto
e src/bar/baz.proto
e produzirá quatro arquivos de saída: build/gen/Foo.pbobjc.h
, build/gen/Foo.pbobjc.m
, build/gen/bar/Baz.pbobjc.h
e build/gen/bar/Baz.pbobjc.m
. O compilador criará automaticamente o diretório build/gen/bar
, se necessário, mas não criará build
ou build/gen
. Eles já devem existir.
Entrega de pacotes
O código Objective-C gerado pelo compilador do buffer de protocolo não é totalmente afetado pelo nome do pacote definido no arquivo .proto
, já que o Objective-C não tem um namespace de linguagem. Em vez disso, os nomes das classes do Objective-C são diferenciados usando prefixos, que você vai descobrir na próxima seção.
Prefixo da classe
Considerando a seguinte opção de arquivo:
option objc_class_prefix = "CGOOP";
A string especificada (neste caso, CGOOP
) é prefixada em frente a todas as classes do Objective-C geradas para esse arquivo .proto
. Use prefixos de três ou mais caracteres, conforme recomendado pela Apple. Todos os prefixos de duas letras são reservados pela Apple.
Conversão de letras concatenadas
O Objective-C idiomático usa letras concatenadas para todos os identificadores.
Os nomes das mensagens não serão convertidos porque o padrão para arquivos proto é nomear as mensagens em CamelCase. Presume-se que o usuário ignorou a convenção por um bom motivo e que a implementação está de acordo com as intenções dele.
Os métodos gerados a partir de nomes de campo e oneof
s, declarações enum
e acessadores de extensão terão seus nomes concatenados. Em geral, para converter um nome de proto em um nome de Objective-C obsoleto em Camel:
- A primeira letra é convertida em maiúsculas (exceto para campos, que sempre começam com uma letra minúscula).
- Para cada sublinhado no nome, o sublinhado é removido e a letra a seguir é maiúscula.
Por exemplo, o campo foo_bar_baz
se torna fooBarBaz
. O campo FOO_bar
se torna fooBar
.
Mensagens
Se a declaração for simples:
message Foo {}
O compilador de buffer de protocolo gera uma classe chamada Foo
. Se você especificar uma opção de arquivo objc_class_prefix
, o valor dela será anexado ao nome da classe gerada.
No caso de mensagens externas com nomes que correspondam a palavras-chave C/C++ ou Objective-C:
message static {}
as interfaces geradas são sufixadas por _Class
da seguinte maneira:
@interface static_Class {}
De acordo com as regras de conversão do caso do Camelo, o nome static
não é convertido. No caso de uma mensagem interna que tem um nome com concatenação que é FieldNumber
ou OneOfCase
, a interface gerada será o nome desse tipo com o sufixo _Class
para garantir que os nomes gerados não entrem em conflito com as enumerações FieldNumber
ou OneOfCase
.
Uma mensagem também pode ser declarada dentro de outra.
message Foo { message Bar {} }
Isso gera:
@interface Foo_Bar : GPBMessage @end
Como você pode ver, o nome da mensagem aninhada é o nome da mensagem (Foo
) anexado com sublinhado (_
) e o nome da mensagem aninhada (Bar
).
Tentamos minimizar o conflito, mas ainda há possíveis casos em que os nomes de mensagens podem entrar em conflito devido à conversão entre sublinhados e letras concatenadas. Por exemplo:
message foo_bar {} message foo { message bar {} }gera
@interface foo_bar
e entra em conflito. A solução mais pragmática pode ser renomear as mensagens conflitantes.Interface GPBMessage
GPBMessage
é a superclasse de todas as classes de mensagem geradas. Ele é necessário para oferecer suporte a um superconjunto da seguinte interface:
@interface GPBMessage : NSObject@end
Os comportamentos dessa interface são os seguintes:
// Will do a deep copy. - (id)copy; // Will perform a deep equality comparison. - (BOOL)isEqual:(id)value;
Campos desconhecidos (somente proto2)
Se uma mensagem criada com uma versão mais antiga da sua definição .proto for analisada com o código gerado por uma versão mais recente (ou vice-versa), a mensagem poderá conter campos opcionais ou repetidos que o código "novo" não reconhece. No código gerado pelo proto2, esses campos não são descartados e são armazenados na propriedade unknownFields
da mensagem.
@property(nonatomic, copy, nullable) GPBUnknownFieldSet *unknownFields;
É possível usar a interface GPBUnknownFieldSet
para buscar esses campos por número ou repetir esses campos como uma matriz.
No proto3, os campos desconhecidos são descartados quando uma mensagem é analisada.
Campos
As seções a seguir descrevem o código gerado pelo compilador de buffer de protocolo para campos de mensagem.
Campos singulares (proto3)
Para cada campo singular, o compilador gera uma propriedade para armazenar dados e uma constante de número inteiro contendo o número do campo. Os campos de tipo de mensagem também recebem uma propriedade has..
que permite verificar se ele está definido na mensagem codificada. Por exemplo, considerando a seguinte mensagem:
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; }
O compilador gerará o seguinte:
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
Casos especiais de nomenclatura
Em alguns casos, as regras de geração de nomes de campo podem resultar em conflitos de nomes, e os nomes precisarão ser "exclusivos". Para resolver esses conflitos, anexe _p
ao final do campo. _p
foi selecionado porque é bastante exclusivo e significa "propriedade".
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 }
gera:
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
Valores padrão
O valor padrão para tipos numéricos é 0
.
O valor padrão para strings é @""
, e o valor padrão para bytes é [NSData data]
.
A atribuição de nil
a um campo de string vai ser declarada na depuração e vai definir o campo como @""
na versão. Atribuir nil
a um campo de bytes será declarado na depuração e definirá o campo como [NSData data]
na versão. Para testar se um campo de bytes ou string está definido, é necessário testar a propriedade de tamanho e compará-lo com 0.
O valor padrão "vazio" de uma mensagem é uma instância dela. Para limpar um valor de mensagem, ele precisa ser definido como nil
. O acesso a uma mensagem limpa retornará uma instância da mensagem padrão, e o método hasFoo
retornará falso.
A mensagem padrão retornada para um campo é uma instância local. O motivo para retornar uma mensagem padrão em vez de nil
é que, no caso de:
message Foo { message Bar { int32 b; } Bar a; }
A implementação será compatível com:
Foo *foo = [[Foo alloc] init]; foo.a.b = 2;
em que a
será criado automaticamente pelos acessadores, se necessário. Se foo.a
retornasse nil
, o padrão do setter foo.a.b
não funcionaria.
Campos singulares (proto2)
Para cada campo singular, o compilador gera uma propriedade para armazenar dados, uma constante de número inteiro contendo o número do campo e uma propriedade has..
que permite verificar se o campo está definido na mensagem codificada. Por exemplo, considerando a seguinte mensagem:
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; }
O compilador gerará o seguinte:
# 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
Casos especiais de nomenclatura
Em alguns casos, as regras de geração de nomes de campo podem resultar em conflitos de nomes, e os nomes precisarão ser "exclusivos". Para resolver esses conflitos, anexe _p
ao final do campo. _p
foi selecionado porque é bastante exclusivo e significa "propriedade".
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 }
gera:
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
Valores padrão (somente campos opcionais)
O valor padrão para tipos numéricos, se nenhum padrão explícito tiver sido especificado pelo usuário, é 0
.
O valor padrão para strings é @""
, e o valor padrão para bytes é [NSData data]
.
A atribuição de nil
a um campo de string vai ser declarada na depuração e vai definir o campo como @""
na versão. Atribuir nil
a um campo de bytes será declarado na depuração e definirá o campo como [NSData data]
na versão. Para testar se um campo de bytes ou string está definido, é necessário testar a propriedade de tamanho e compará-lo com 0.
O valor padrão "vazio" de uma mensagem é uma instância dela. Para limpar um valor de mensagem, ele precisa ser definido como nil
. O acesso a uma mensagem limpa retornará uma instância da mensagem padrão, e o método hasFoo
retornará falso.
A mensagem padrão retornada para um campo é uma instância local. O motivo para retornar uma mensagem padrão em vez de nil
é que, no caso de:
message Foo { message Bar { int32 b; } Bar a; }
A implementação será compatível com:
Foo *foo = [[Foo alloc] init]; foo.a.b = 2;
em que a
será criado automaticamente pelos acessadores, se necessário. Se foo.a
retornasse nil
, o padrão do setter foo.a.b
não funcionaria.
Campos repetidos
Assim como campos singulares (proto2 proto3), o compilador de buffer de protocolo gera uma propriedade de dados para cada campo repetido. Essa propriedade de dados é um GPB<VALUE>Array
, dependendo do tipo de campo, em que <VALUE>
pode ser UInt32
, Int32
, UInt64
, Int64
, Bool
, Float
, Double
ou Enum
. NSMutableArray
será usado para os tipos string
, bytes
e message
. Os nomes de campos de tipos repetidos têm Array
anexados. O motivo para anexar Array
na interface Objective-C é tornar o código mais legível. Campos repetidos em arquivos proto tendem a ter nomes no singular que não são bem lidos no uso padrão do Objective-C. Colocar nomes no plural seria mais idiomático em Objective-C, mas as regras de pluralização são muito complexas para serem compatíveis com o compilador.
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; }
gera:
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
Para campos de string, bytes e mensagem, os elementos da matriz são NSString*
, NSData*
e ponteiros para subclasses de GPBMessage
, respectivamente.
Valores padrão
O valor padrão de um campo repetido precisa estar vazio. No código gerado no Objective-C, este é um GPB<VALUE>Array
vazio. Se você acessar um campo repetido vazio, receberá uma matriz vazia que pode ser atualizada como qualquer outra matriz de campo repetido.
Foo *myFoo = [[Foo alloc] init]; [myFoo.stringValueArray addObject:@"A string"]
Também é possível usar a propriedade <field>Array_Count
fornecida para verificar se a matriz de um campo repetido específico está vazia sem precisar criá-la:
if (myFoo.messageValueArray_Count) { // There is something in the array... }
Interface GPB<VALUE>Array
As GPB<VALUE>Array
s (além de GPBEnumArray
, que veremos abaixo) têm a seguinte interface:
@interface GPBArray : NSObject @property (nonatomic, readonly) NSUInteger count; + (instancetype)array; + (instancetype)arrayWithValue:( )value; + (instancetype)arrayWithValueArray:(GPB Array *)array; + (instancetype)arrayWithCapacity:(NSUInteger)count; // Initializes the array, copying the values. - (instancetype)initWithValueArray:(GPB Array *)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:(GPB Array *)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
tem uma interface um pouco diferente para processar a função de validação e acessar valores brutos.
@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
Campos Oneof
Você recebeu uma mensagem com definições de campo oneof:
message Order { oneof OrderID { string name = 1; int32 address = 2; }; int32 quantity = 3; };
O compilador de buffer de protocolo gera:
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);
Definir uma das propriedades limpará todas as outras associadas a ela.
<ONE_OF_NAME>_OneOfCase_GPBUnsetOneOfCase
sempre vai ser equivalente a 0 para permitir testes fáceis e ver se algum campo em um deles está definido
Campos do mapa
Para esta definição de mensagem:
message Bar {...} message Foo { map<int32, string> a_map = 1; map<string, Bar> b_map = 2; };
O compilador gera o seguinte:
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
Os casos em que as chaves são strings e os valores são strings, bytes ou mensagens são processados por NSMutableDictionary
.
Outros casos são:
GBP<KEY><VALUE>Dictionary
onde:
<KEY>
é Uint32, Int32, UInt64, Int64, Bool ou String.<VALUE>
é UInt32, Int32, UInt64, Int64, Bool, Float, Double, Enum ou Object. OObject
é usado para valores do tipostring
bytes
oumessage
para reduzir o número de classes e está alinhado com a forma como o Objective-C funciona comNSMutableDictionary
.
Valores padrão
O valor padrão de um campo do mapa está vazio. No código gerado no Objective-C, este é um GBP<KEY><VALUE>Dictionary
vazio. Se você acessar um campo de mapa vazio, vai receber um dicionário vazio que pode ser atualizado como qualquer outro campo do mapa.
Também é possível usar a propriedade <mapField$gt;_Count
fornecida para verificar se um determinado mapa está vazio:
if (myFoo.myMap_Count) { // There is something in the map... }.
Interface GBP<KEY><VALUE>Dictionary
A interface GBP<KEY><VALUE>Dictionary
(exceto GBP<KEY>ObjectDictionary
e GBP<KEY>EnumDictionary
) é a seguinte:
@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
A interface 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
tem uma interface um pouco diferente para processar a função de validação e acessar valores brutos.
@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
Enumerações
Com uma definição de enumeração como:
enum Foo { VALUE_A = 0; VALUE_B = 1; VALUE_C = 5; }
O código gerado será:
// 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();
Cada enumeração tem uma função de validação declarada:
// Returns YES if the given numeric value matches one of Foo's // defined values (0, 1, 5). BOOL Foo_IsValidValue(int32_t value);
e uma função de acesso do descritor de enumeração declarada para ele:
// 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)();
As funções de acesso do descritor de enumeração são funções C, ao contrário dos métodos na classe de enumeração, porque raramente são usadas pelo software cliente. Isso reduzirá a quantidade de informações de tempo de execução do Objective-C geradas e permitirá que o vinculador as remova.
No caso de enumerações externas com nomes correspondentes a qualquer palavra-chave C/C++ ou Objective-C, como:
enum method {}
As interfaces geradas são sufixadas com _Enum
da seguinte maneira:
// The generated enumeration name is the keyword suffixed by _Enum. typedef GPB_ENUM(Method_Enum) {}
Uma enumeração também pode ser declarada dentro de outra mensagem. Por exemplo:
message Foo { enum Bar { VALUE_A = 0; VALUE_B = 1; VALUE_C = 5; } Bar aBar = 1; Bar aDifferentBar = 2; repeated Bar aRepeatedBar = 3; }
gera:
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);
Todos os campos de enumeração podem acessar o valor como um enumerador digitado (Foo_Bar
no exemplo acima) ou, se estiver usando proto3, como um valor int32_t
bruto (usando as funções de acessador no exemplo acima). Isso vai oferecer suporte ao caso em que o servidor retorna valores que o cliente pode não reconhecer devido à compilação do cliente e do servidor com versões diferentes do arquivo proto.
Os valores de enumeração não reconhecidos são tratados de forma diferente dependendo da versão dos buffers de protocolo usada. No proto3, kGPBUnrecognizedEnumeratorValue
é retornado para o valor do enumerador digitado se o valor do enumerador nos dados da mensagem analisada não for aquele que o código que está lendo foi compilado para oferecer suporte. Se o valor real for desejado, use os acessadores de valor bruto para conseguir o valor como um int32_t
. Se você estiver usando proto2, os valores de enumeração não reconhecidos serão tratados como campos desconhecidos.
kGPBUnrecognizedEnumeratorValue
é definido como 0xFBADBEEF
e será um erro se algum numerador em uma enumeração tiver esse valor. A tentativa de definir qualquer campo de enumeração para esse valor é um erro de execução. Da mesma forma, tentar definir qualquer campo de enumeração para um enumerador não definido pelo tipo de enumeração usando os acessadores tipados é um erro de tempo de execução. Nos dois casos de erro, os builds de depuração vão causar uma declaração, e os builds de lançamento vão registrar e definir o campo como o valor padrão (0
).
Os acessadores de valor bruto são definidos como funções C e não como métodos Objective-C, porque não são usados na maioria dos casos. Declará-los como funções C reduz o desperdício de informações do ambiente de execução do Objective-C e permite que o vinculador os elimine.
Suporte à enumeração Swift
A Apple documenta como importam enumerações Objective-C para enumerações Swift em Como interagir com APIs C. As enumerações geradas por buffer de protocolo aceitam conversões do Objective-C para Swift.// Proto enum Foo { VALUE_A = 0; }
gera:
// Objective-C typedef GPB_ENUM(Foo) { Foo_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue, Foo_ValueA = 0, };
que, no código Swift, permitirá:
// Swift let aValue = Foo.ValueA let anotherValue: Foo = .GPBUnrecognizedEnumeratorValue
Tipos conhecidos (somente proto3)
Se você usar um dos tipos de mensagem fornecidos com o proto3, eles geralmente usarão apenas as definições de proto no código do Objective-C gerado, embora forneçamos alguns métodos básicos de conversão em categorias para simplificar o uso. Ainda não temos APIs especiais para todos os tipos conhecidos, inclusive Any
. No momento, não há um método auxiliar para converter o valor de mensagem de Any
em uma mensagem do tipo apropriado.
Carimbos de data/hora
@interface GPBTimeStamp (GPBWellKnownTypes) @property (nonatomic, readwrite, strong) NSDate *date; @property (nonatomic, readwrite) NSTimeInterval timeIntervalSince1970; - (instancetype)initWithDate:(NSDate *)date; - (instancetype)initWithTimeIntervalSince1970: (NSTimeInterval)timeIntervalSince1970; @end
Duração
@interface GPBDuration (GPBWellKnownTypes) @property (nonatomic, readwrite) NSTimeInterval timeIntervalSince1970; - (instancetype)initWithTimeIntervalSince1970: (NSTimeInterval)timeIntervalSince1970; @end
Extensões (somente proto2)
Você recebeu uma mensagem com um intervalo de extensões:
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; } }
O compilador gera o seguinte:
# 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
Para acessar e definir esses campos de extensão, use o seguinte:
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]]);