OBSERVAÇÃO:este site foi descontinuado. O site será desativado após 31 de janeiro de 2023, e o tráfego será redirecionado para o novo site em https://protobuf.dev. Enquanto isso, as atualizações serão feitas apenas para protobuf.dev.

Código gerado pelo Objective-C

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

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 por pbobjc.h ou pbobjc.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 oneofs, 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>Arrays (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:(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 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. O Object é usado para valores do tipo string bytes ou message para reduzir o número de classes e está alinhado com a forma como o Objective-C funciona com NSMutableDictionary.

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