Como usar palavras-chave Objective-C não nulas e anuláveis ​​no método de API baseado em bloco

105

Considere o seguinte método

- (void)methodWithArg:(NSString *)arg1 andArg:(NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

Com as palavras-chave novas nonnulle de nullable anotação , podemos enriquecê-lo da seguinte maneira:

- (void)methodWithArg:(nonnull NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

mas também recebemos este aviso:

O ponteiro está sem um especificador de tipo de nulidade (__nonnull ou __nullable)

Refere-se ao terceiro parâmetro (o bloco um).

A documentação não cobre com exemplos como especificar a nulidade dos parâmetros de bloco. Afirma literalmente

Você pode usar as formas não sublinhadas anuláveis ​​e não nulas imediatamente após um parêntese aberto, desde que o tipo seja um objeto simples ou um ponteiro de bloco.

Tentei colocar uma das duas palavras-chave para o bloco (em qualquer posição) sem sucesso. Também tentei as variantes prefixadas de sublinhado (__nonnull e __nullable).

Portanto, minha pergunta é: como posso especificar a semântica de nulidade para parâmetros de bloco?

Albertodebortoli
fonte

Respostas:

128

Isso parece estar funcionando

- (void)methodWithArg:(nonnull NSString *)arg1 
  andArg:(nullable NSString *)arg2 completionHandler:(nullable void (^)
  (NSArray * _Nullable results, NSError * _Nonnull error))completionHandler

Você precisa especificar a nulidade para o bloco e seus parâmetros ...

EDITAR: Para obter mais informações, consulte Swift Blog

Fabio Ritrovato
fonte
Como isso funciona com o NSError **tipo? Não consigo deixar o compilador feliz.
duhanebel
3
De acordo com o blog do Swift: The particular type NSError ** is so often used to return errors via method parameters that it is always assumed to be a nullable pointer to a nullable NSError reference. developer.apple.com/swift/blog/?id=25
user1687195
@duhanebel A resposta é dada em stackoverflow.com/questions/33198597/… : (NSError * _Nullable * _Nullable) error
Elise van Looij
33

De acordo com o Blog da Apple ("Nullability and Objective-C") , você pode usar

NS_ASSUME_NONNULL_BEGINe NS_ASSUME_NONNULL_END.

Dentro dessas regiões, qualquer tipo de ponteiro simples será considerado nonnull. Então você pode apenas adicionar nullablepara objeto anulável, que como

NS_ASSUME_NONNULL_BEGIN

@interface MyClass: NSObject

- (void)methodWithArg:(NSString *)arg1 andArg:(nullable NSString *)arg2 completionHandler:(void (^)(NSArray *results, NSError *error))completionHandler;

@end

NS_ASSUME_NONNULL_END
  • se o erro for NSError **tipo, deve serNSError * _Nullable * _Nullable
  • se o objeto for id *tipo, melhor usar id _Nullable * _Nonnull, depende (pode ser que você queira um _Nullable id * _Nullabletipo).
  • se o objeto é o NSObject *tipo, você precisa colocar uma anotação após o ponteiro, como esteNSObject * _Nullable * _Nonnull

Nota

_Nonnulle _Nullabledeve ser usado após o ponteiro ou id(a Apple usa no código de exemplo AAPLListItem * _Nullable), mas os formulários não sublinhados nonnulle nullablepodem ser usados ​​após um parêntese aberto.

No entanto, no caso comum, há uma maneira muito mais agradável de escrever essas anotações: dentro das declarações de método, você pode usar os formulários não sublinhados nullablee nonnullimediatamente após um parêntese aberto, desde que o tipo seja um objeto simples ou ponteiro de bloco.

verifique mais em "Anulabilidade e Objective-C"

Por segurança, existem algumas exceções a esta regra:

  • typedeftipos geralmente não têm uma nulidade inerente - eles podem ser facilmente anuláveis ​​ou não anuláveis, dependendo do contexto. Portanto, typedefnão se presume que os tipos sejam nonnull, mesmo dentro das regiões auditadas.
  • Tipos de ponteiro mais complexos, como id *devem ser anotados explicitamente. Por exemplo, para especificar um ponteiro não anulável para uma referência de objeto anulável, use_Nullable id * _Nonnull .
  • O tipo específico NSError **é tão frequentemente usado para retornar erros por meio de parâmetros de método que sempre é considerado um ponteiro anulável para uma NSErrorreferência anulável .

O _Nullable id * _Nonnullpode ser confundido, id _Nullable * _Nonnullé melhor compreensão.

_Nonnulle _Nullabledeve ser usado após o ponteiro ou id(a Apple faz no código de exemploAAPLListItem * _Nullable )

likid1412
fonte
Para propriedade fraca, _Nullable aplicado.
Dongjin Suh
3

Você também pode fazer assim:

- (id __nullable)methodWithArg:(NSString * __nullable)arg1
                        andArg:(NSString * __nonnull)arg2
             completionHandler:(void (^ __nonnull)(NSArray * __nonnull results, NSError * __nullable error))completionHandler;

Depende apenas de qual sintaxe você gosta mais.

OlDor
fonte
2

Para definir conclusões em um arquivo de cabeçalho, fiz isso

typedef void (^PublicEventsHandler) (BOOL success, NSArray * _Nullable publicEvents);

Claro, concordo com a resposta aceita.

Krishna Kumar
fonte
0

Do blog do desenvolvedor da apple : The Core: _Nullable and _Nonnull

você pode usar as formas não sublinhadas anuláveis ​​e não nulas imediatamente após um parêntese aberto , desde que o tipo seja um objeto simples ou um ponteiro de bloco.

Os formulários não sublinhados são melhores do que os sublinhados, mas você ainda precisa aplicá-los a todos os tipos em seu cabeçalho .

Parag Bafna
fonte
Sim, mas os não sublinhados (mais agradáveis) não funcionam em declarações em bloco
Paul Bruneau
-2

Aqui está o que usei para o caso NSError **:

-(BOOL) something:(int)number withError:(NSError *__autoreleasing  __nullable * __nullable)error;
Kevin
fonte
1
Como disse a Apple , NSError **não é necessário especificar sua nulidade.
DawnSong