Différence entre nullable, __nullable et _Nullable dans Objective-C

Avec Xcode 6.3, de nouvelles annotations ont été introduites pour mieux exprimer l’intention des API dans Objective-C (et pour assurer une meilleure prise en charge de Swift bien sûr). Ces annotations étaient bien sûr nonnull nullable , null_unspecified et null_unspecified .

Mais avec Xcode 7, de nombreux avertissements apparaissent, tels que:

Il manque au pointeur un spécificateur de type de nullité (_Nonnull, _Nullable ou _Null_unspecified).

En plus de cela, Apple utilise un autre type de spécificateurs de nullité, marquant leur code C ( source ):

CFArrayRef __nonnull CFArrayCreate( CFAllocatorRef __nullable allocator, const void * __nonnull * __nullable values, CFIndex numValues, const CFArrayCallBacks * __nullable callBacks);

Donc, pour résumer, nous avons maintenant ces 3 annotations de nullité différentes:

  • nonnull , null_unspecified , null_unspecified
  • _Nonnull , _Nullable , _Null_unspecified
  • __nonnull , __nullable , __null_unspecified

Même si je sais pourquoi et où utiliser chaque annotation, je suis un peu confus par le type d’annotations que je devrais utiliser, où et pourquoi. C’est ce que j’ai pu rassembler:

  • Pour les propriétés, je dois utiliser nonnull , null_unspecified , null_unspecified .
  • Pour les parameters de méthode, je dois utiliser nonnull , null_unspecified , null_unspecified .
  • Pour les méthodes C, je devrais utiliser __nonnull , __nullable , __null_unspecified .
  • Pour les autres cas, tels que les doubles pointeurs, je devrais utiliser _Nonnull , _Nullable , _Null_unspecified .

Mais je ne comprends toujours pas pourquoi nous avons tant d’annotations qui font essentiellement la même chose.

Donc ma question est:

Quelle est la différence exacte entre ces annotations, comment les placer correctement et pourquoi?

De la documentation de clang :

Les qualificatifs de nullité (type) indiquent si une valeur d’un type de pointeur donné peut être nulle (le qualificatif _Nullable ), n’a pas de signification définie pour null (le qualificatif _Nonnull ) ou pour laquelle l’objective de null n’est pas clair (le _Null_unspecified ). Étant donné que les qualificatifs de nullité sont exprimés dans le système de types, ils sont plus généraux que les nonnull et returns_nonnull , ce qui permet d’exprimer (par exemple) un pointeur nullable sur un tableau de pointeurs non normaux. Les qualificatifs de nullité sont écrits à droite du pointeur auquel ils s’appliquent.

, et

Dans Objective-C, il existe une autre orthographe pour les qualificateurs de nullité qui peuvent être utilisés dans les méthodes et les propriétés Objective-C à l’aide de mots-clés non soulignés et sensibles au contexte.

Ainsi, pour les retours de méthode et les parameters, vous pouvez utiliser les versions à double soulignement __nonnull / __nullable / __null_unspecified au lieu des versions à soulignement simple ou des non-soulignées. La différence est que les soulignés simples et doubles doivent être placés après la définition du type, tandis que les non soulignés doivent être placés avant la définition du type.

Ainsi, les déclarations suivantes sont équivalentes et sont correctes:

 - (nullable NSNumber *)result - (NSNumber * __nullable)result - (NSNumber * _Nullable)result 

Pour les parameters:

 - (void)doSomethingWithSsortingng:(nullable NSSsortingng *)str - (void)doSomethingWithSsortingng:(NSSsortingng * _Nullable)str - (void)doSomethingWithSsortingng:(NSSsortingng * __nullable)str 

Pour les propriétés:

 @property(nullable) NSNumber *status @property NSNumber *__nullable status @property NSNumber * _Nullable status 

Les choses se compliquent cependant lorsque des doubles pointeurs ou des blocs renvoyant quelque chose de différent de nul sont impliqués, car ceux qui ne le sont pas ne sont pas autorisés ici:

 - (void)compute:(NSError * _Nullable * _Nullable)error - (void)compute:(NSError * __nullable * _Null_unspecified)error; // and all other combinations 

Comme pour les méthodes qui acceptent les blocs en tant que parameters, veuillez noter que le nonnull / nonnull s’applique au bloc, et non à son type de retour. Par conséquent, les éléments suivants sont équivalents:

 - (void)executeWithCompletion:(nullable void (^)())handler - (void)executeWithCompletion:(void (^ _Nullable)())handler - (void)executeWithCompletion:(void (^ __nullable)())handler 

Si le bloc a une valeur de retour, vous êtes forcé dans l’une des versions de soulignement:

 - (void)convertObject:(nullable id __nonnull (^)(nullable id obj))handler - (void)convertObject:(id __nonnull (^ _Nullable)())handler - (void)convertObject:(id _Nonnull (^ __nullable)())handler // the method accepts a nullable block that returns a nonnull value // there are some more combinations here, you get the idea 

En conclusion, vous pouvez utiliser l’un ou l’autre, à condition que le compilateur puisse déterminer l’élément auquel atsortingbuer le qualificatif.

Du blog Swift :

Cette fonctionnalité a été publiée pour la première fois dans Xcode 6.3 avec les mots-clés __nullable et __nonnull. En raison de conflits potentiels avec des bibliothèques tierces, nous les avons modifiées dans Xcode 7 en _Nullable et _Nonnull que vous voyez ici. Cependant, pour des raisons de compatibilité avec Xcode 6.3, nous avons prédéfini des macros __nullable et __nonnull pour développer les nouveaux noms.

J’ai vraiment aimé cet article , alors je montre simplement ce que l’auteur a écrit: https://swiftunboxed.com/interop/objc-nullability-annotations/

  • null_unspecified: ponts vers un Swift implicitement déballé en option. Ceci est la valeur par défaut .
  • nonnull : la valeur ne sera pas nulle; des ponts vers une référence régulière.
  • nullable : la valeur peut être nulle; ponts à une option.
  • null_resettable : la valeur ne peut jamais être nulle lors de la lecture, mais vous pouvez la définir sur zéro pour la réinitialiser. S’applique uniquement aux propriétés.

Les notations ci-dessus diffèrent alors si vous les utilisez dans le contexte des propriétés ou des fonctions / variables:

Notation vs. Propriétés

L’auteur de l’article a également fourni un bon exemple:

 // property style @property (nonatomic, strong, null_resettable) NSSsortingng *name; // pointer style + (NSArray * _Nullable)interestingObjectsForKey:(NSSsortingng * _Nonnull)key; // these two are equivalent! @property (nonatomic, strong, nullable) NSSsortingng *identifier1; @property (nonatomic, strong) NSSsortingng * _Nullable identifier2; 

Très pratique est

 NS_ASSUME_NONNULL_BEGIN 

et en fermant avec

 NS_ASSUME_NONNULL_END 

Cela annulera le besoin du niveau de code ‘nullibis’ 🙂 car il est en quelque sorte logique de supposer que tout est non nul (ou non nul ou _nonnull ou __nonnull ) sauf indication contraire.

Malheureusement, il y a des exceptions à cela aussi …

  • typedef s ne sont pas supposés être __nonnull (note, nonnull ne semble pas fonctionner, il faut utiliser son demi-frère moche)
  • id * besoin d’un nullibi explicite mais wow la taxe de péché ( _Nullable id * _Nonnull <- devinez ce que cela signifie ...)
  • NSError ** est toujours supposé nullable

Donc, avec les exceptions aux exceptions et les mots-clés incohérents provoquant la même fonctionnalité, peut-être l’approche consiste-t-elle à utiliser les versions laides __nonnull / __nullable / __null_unspecified et à échanger lorsque le complicateur se plaint …? Peut-être que c’est pourquoi ils existent dans les en-têtes Apple?

Chose intéressante, quelque chose l’a mis dans mon code … J’ai horreur de souligner dans le code (ancien type Apple C ++ style guy) donc je suis absolument sûr de ne pas les avoir tapé mais ils sont apparus (un exemple de plusieurs):

 typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential ); 

Et encore plus intéressant, où il a inséré le __nullable est faux … (eek @!)

Je voudrais vraiment pouvoir utiliser la version sans soulignement mais apparemment, cela ne vole pas avec le compilateur car cela est signalé comme une erreur:

 typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * nonnull credential );