web-dev-qa-db-fra.com

Comment utiliser des mots clés Objective-C non null et nullable dans une méthode d'API basée sur des blocs

Considérons la méthode suivante

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

Avec les nouveaux nonnull et nullablemots-clés d'annotation , nous pouvons l'enrichir comme suit:

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

mais nous avons aussi cet avertissement:

Il manque un spécificateur de type nullable au pointeur (__nonnull ou __nullable)

Il fait référence au troisième paramètre (le bloc un).

La documentation ne couvre pas avec des exemples comment spécifier la nullité des paramètres de bloc. Il dit textuellement

Vous pouvez utiliser les formes non soulignées nullable et nonnull immédiatement après une parenthèse ouverte, à condition que le type soit un objet simple ou un pointeur de bloc.

J'ai essayé de mettre l'un des deux mots clés pour le bloc (dans n'importe quelle position) sans aucune chance. Nous avons également essayé les variantes avec préfixe de soulignement (__nonnull et __nullable).

Par conséquent, ma question est la suivante: comment puis-je spécifier la sémantique d'invalidité pour les paramètres de bloc?

93
albertodebortoli

Cela à l'air de marcher

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

Vous devez spécifier la nullabilité du bloc et de ses paramètres ...

EDIT: Pour plus d'informations, voir Swift Blog

114
Fabio Ritrovato

Selon Blog Apple ("Nullability and Objective-C") , vous pouvez utiliser

_NS_ASSUME_NONNULL_BEGIN_ et _NS_ASSUME_NONNULL_END_.

Dans ces régions, tout type de pointeur simple sera supposé être nonnull. Ensuite, vous pouvez simplement ajouter nullable pour un objet nullable, qui

_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
_
  • si l'erreur est de type _NSError **_, doit être _NSError * _Nullable * _Nullable_
  • si l'objet est de type _id *_, mieux utiliser _id _Nullable * _Nonnull_, cela dépend (vous voudrez peut-être un type __Nullable id * _Nullable_).
  • si object est de type _NSObject *_, vous devez mettre une annotation après le pointeur, comme ceci _NSObject * _Nullable * _Nonnull_

Remarque

__Nonnull_ et __Nullable_ doivent être utilisés après le pointeur ou id (Apple le fait dans l'exemple de code _AAPLListItem * _Nullable_), mais les formes non soulignées nonnull et nullable peut être utilisé après une parenthèse ouverte.

Cependant, dans le cas courant, il existe un moyen bien plus agréable d'écrire ces annotations: dans les déclarations de méthode, vous pouvez utiliser les formes non soulignées nullable et nonnull immédiatement après une parenthèse ouverte, tant que le type est un objet simple ou un pointeur de bloc.

vérifier plus dans "Nullability and Objective-C"

Pour des raisons de sécurité, il existe quelques exceptions à cette règle:

  • Les types typedef n’ont généralement pas de caractère nullable inhérent - ils peuvent facilement être soit nullables, soit non-nullables en fonction du contexte. Par conséquent, les types typedef ne sont pas supposés être nonnull, même dans les régions contrôlées.
  • Les types de pointeurs plus complexes tels que _id *_ doivent être explicitement annotés. Par exemple, pour spécifier un pointeur non nullable sur une référence d'objet nullable, utilisez __Nullable id * _Nonnull_.
  • Le type particulier _NSError **_ est si souvent utilisé pour renvoyer des erreurs via des paramètres de méthode qu'il est toujours supposé être un pointeur nullable vers une référence nullable NSError.

Le __Nullable id * _Nonnull_ peut être confondu, _id _Nullable * _Nonnull_ est une meilleure compréhension.

_Nonnull_ ET __Nullable_ DOIT ÊTRE UTILISÉ APRÈS LE POINTEUR OU id (APPLE LE FAIT DANS L'EXEMPLE DE CODE _AAPLListItem * _Nullable)

29
likid1412

Vous pouvez aussi faire comme ça:

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

Cela dépend seulement de la syntaxe que vous préférez.

3
OlDor

Pour définir les complétions dans un fichier d'en-tête, je l'ai fait

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

Bien sûr, je suis d'accord avec la réponse acceptée.

2
Krishna Kumar

De Apple blog de développeur : Le noyau: _Nullable et _Nonnull

vous pouvez utiliser les formes non soulignées nullable et nonnull immédiatement après une parenthèse ouverte , tant que le type est un objet simple ou un pointeur de bloc.

Les formulaires non soulignés sont plus agréables que ceux sous soulignés, mais vous devez toujours les appliquer à chaque type de votre en-tête .

0
Parag Bafna