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:
nonnull
, null_unspecified
, null_unspecified
. nonnull
, null_unspecified
, null_unspecified
. __nonnull
, __nullable
, __null_unspecified
. _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 lesnonnull
etreturns_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:
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 );