Differenza tra nullable, __nullable e _Nullable in Objective-C


154

Con Xcode 6.3 sono state introdotte nuove annotazioni per esprimere meglio l'intenzione delle API in Objective-C (e per garantire un miglior supporto rapido, ovviamente). Quelle annotazioni erano ovviamente nonnull, nullablee null_unspecified.

Ma con Xcode 7, appaiono molti avvisi come:

Nel puntatore manca un identificatore del tipo di nullability (_Nonnull, _Nullable o _Null_unspecified).

Inoltre, Apple utilizza un altro tipo di identificatori di nullability, contrassegnando il loro codice C ( sorgente ):

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

Quindi, per riassumere, ora abbiamo queste 3 diverse annotazioni di nullability:

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

Anche se so perché e dove utilizzare quale annotazione, mi sto confondendo leggermente con quale tipo di annotazioni dovrei usare, dove e perché. Questo è ciò che potrei raccogliere:

  • Per le proprietà devo usare nonnull, nullable, null_unspecified.
  • Per i parametri del metodo devo usare nonnull, nullable, null_unspecified.
  • Per i metodi C devo usare __nonnull, __nullable, __null_unspecified.
  • Per gli altri casi, come ad esempio doppi puntatori devo usare _Nonnull, _Nullable, _Null_unspecified.

Ma sono ancora confuso sul perché abbiamo così tante annotazioni che sostanzialmente fanno la stessa cosa.

Quindi la mia domanda è:

Qual è la differenza esatta tra quelle annotazioni, come posizionarle correttamente e perché?


3
Ho letto quel post, ma non spiega la differenza e perché ora abbiamo 3 diversi tipi di annotazioni e voglio davvero capire perché hanno continuato ad aggiungere il terzo tipo.
Legoless,

2
Questo in realtà non aiuta @ Cy-4AH e lo sai. :)
Legoless,

@Legoless, sei sicuro di leggerlo attentamente? spiega esattamente dove e come dovresti usarli, quali sono gli ambiti controllati, quando puoi usare l'altro per una migliore leggibilità, motivi di compatibilità, ecc. ecc. potresti non sapere cosa ti piace davvero chiedere, ma il la risposta è chiaramente sotto il link. forse sono solo io, ma non credo che sarebbero necessarie ulteriori spiegazioni per spiegare il loro scopo, copiare e incollare quella semplice spiegazione qui come una risposta qui sarebbe davvero imbarazzante, immagino. :(
holex,

2
Risolve alcune parti, ma no, ancora non capisco perché non abbiamo solo le prime annotazioni. Spiega solo perché sono passati da __nullable a _Nullable, ma non perché abbiamo nemmeno bisogno di _Nullable, se abbiamo nullable. Inoltre, non spiega perché Apple utilizza ancora __nullable nel proprio codice.
Legoless,

Risposte:


152

Dalla clang documentazione :

I qualificatori di nullability (tipo) indicano se un valore di un determinato tipo di puntatore può essere null (il _Nullablequalificatore), non ha un significato definito per null (il _Nonnullqualificatore) o per il quale lo scopo di null non è chiaro (il _Null_unspecifiedqualificatore) . Poiché i qualificatori di nullability sono espressi all'interno del sistema di tipi, sono più generali degli attributi nonnulle returns_nonnull, consentendo di esprimere (ad esempio) un puntatore nullable a una matrice di puntatori non null. I qualificatori di nullità sono scritti a destra del puntatore a cui si applicano.

, e

In Objective-C, esiste un'ortografia alternativa per i qualificatori di nullability che possono essere utilizzati nei metodi e nelle proprietà Objective-C utilizzando parole chiave sensibili al contesto e non sottolineate

Pertanto, per i ritorni e i parametri del metodo è possibile utilizzare le versioni con sottolineatura doppia __nonnull/ __nullable/ __null_unspecifiedanziché quelle con sottolineatura singola, oppure anziché quelle con sottolineatura. La differenza è che i caratteri di sottolineatura singoli e doppi devono essere posizionati dopo la definizione del tipo, mentre quelli non sottolineati devono essere posizionati prima della definizione del tipo.

Pertanto, le seguenti dichiarazioni sono equivalenti e corrette:

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

Per parametri:

- (void)doSomethingWithString:(nullable NSString *)str
- (void)doSomethingWithString:(NSString * _Nullable)str
- (void)doSomethingWithString:(NSString * __nullable)str

Per le proprietà:

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

Le cose tuttavia complicano quando sono coinvolti doppi puntatori o blocchi che restituiscono qualcosa di diverso dal vuoto, poiché qui non sono ammessi quelli non di sottolineatura:

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

Simile ai metodi che accettano blocchi come parametri, si noti che nonnullnullable qualificatore / si applica al blocco e non al tipo di ritorno, quindi i seguenti sono equivalenti:

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

Se il blocco ha un valore di ritorno, sei forzato in una delle versioni di sottolineatura:

- (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

In conclusione, è possibile utilizzare uno dei due, purché il compilatore possa determinare l'elemento a cui assegnare il qualificatore.


2
Sembra che le versioni di sottolineatura possano essere utilizzate ovunque, quindi suppongo che le userò coerentemente, piuttosto che usare le versioni di sottolineatura in alcuni punti e quelle senza il carattere di sottolineatura in altri. Corretta?
Vaddadi Kartick,

@KartickVaddadi sì, esatto, puoi utilizzare costantemente la versione con sottolineatura singola o doppia.
Cristik,

_Null_unspecifiedin Swift questo si traduce in facoltativo? non opzionale o cosa?
Miele

1
@Honey _Null_unspecified viene importato in Swift come Implicitamente non
confezionato

1
@Cristik aha. Immagino che sia il suo valore predefinito ... perché quando non lo avevo specificato stavo diventando implicitamente non imballato facoltativo ...
Miele

28

Dal blog Swift :

Questa funzione è stata inizialmente rilasciata in Xcode 6.3 con le parole chiave __nullable e __nonnull. A causa di potenziali conflitti con librerie di terze parti, le abbiamo modificate in Xcode 7 in _Nullable e _Nonnull che vedi qui. Tuttavia, per compatibilità con Xcode 6.3 abbiamo macro predefinite __nullable e __nonnull per espandere i nuovi nomi.


4
In breve, le versioni singole e doppie sottolineate sono identiche.
Vaddadi Kartick,

4
Documentato anche nelle note di rilascio di Xcode 7.0: "I qualificatori di nullità con sottolineatura doppia (__nullable, __nonnull e __null_unspecified) sono stati rinominati per utilizzare un singolo carattere di sottolineatura con una lettera maiuscola: _Nullable, _Nonnull e _Null_unspecified, rispettivamente). Il compilatore precede i macros mappatura dai vecchi nomi non specificati doppi ai nuovi nomi per la compatibilità dei sorgenti. (21530726) "
Cosyn,

25

Mi è piaciuto molto questo articolo , quindi sto semplicemente mostrando ciò che l'autore ha scritto: https://swiftunboxed.com/interop/objc-nullability-annotations/

  • null_unspecified:getti un ponte su uno Swift implicitamente non scartato opzionale. Questo è il valore predefinito .
  • nonnull: il valore non sarà nullo; collega un riferimento regolare.
  • nullable: il valore può essere nullo; passa a un facoltativo.
  • null_resettable: il valore non può mai essere zero durante la lettura, ma è possibile impostarlo su zero per ripristinarlo. Si applica solo alle proprietà.

Le notazioni sopra, quindi differiscono se le usi nel contesto di proprietà o funzioni / variabili:

Notazione puntatori vs. proprietà

L'autore dell'articolo ha anche fornito un bell'esempio:

// property style
@property (nonatomic, strong, null_resettable) NSString *name;

// pointer style
+ (NSArray<NSView *> * _Nullable)interestingObjectsForKey:(NSString * _Nonnull)key;

// these two are equivalent!
@property (nonatomic, strong, nullable) NSString *identifier1;
@property (nonatomic, strong) NSString * _Nullable identifier2;

12

È molto utile

NS_ASSUME_NONNULL_BEGIN 

e chiudendo con

NS_ASSUME_NONNULL_END 

Ciò annullerà la necessità del livello di codice "nullibis" :-) dato che ha senso supporre che tutto sia non nullo (o nonnullo _nonnullo __nonnull) se non diversamente indicato.

Purtroppo ci sono anche delle eccezioni a questo ...

  • typedefs non si presume che sia __nonnull(nota, nonnullnon sembra funzionare, deve usare il suo brutto fratellastro)
  • id *ha bisogno di un esplicito nullibi ma stupisce la tassa sul peccato ( _Nullable id * _Nonnull<- indovina cosa significa ...)
  • NSError ** è sempre assunto nullable

Quindi, con le eccezioni alle eccezioni e le parole chiave incoerenti che suscitano la stessa funzionalità, forse l'approccio è quello di utilizzare le brutte versioni __nonnull/ __nullable/ __null_unspecifiede scambiare quando il complier si lamenta ...? Forse è per questo che esistono nelle intestazioni Apple?

È interessante notare che qualcosa lo ha inserito nel mio codice ... Io detesto i caratteri di sottolineatura nel codice (tipo di Apple C ++ in stile vecchia scuola) quindi sono assolutamente sicuro di non averli digitati ma sono apparsi (un esempio di diversi):

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

E ancora più interessante, dove è stato inserito il __nullable è sbagliato ... (eek @!)

Vorrei davvero poter usare solo la versione non di sottolineatura, ma a quanto pare non vola con il compilatore in quanto questo è contrassegnato come errore:

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

2
Il carattere di sottolineatura può essere utilizzato direttamente solo dopo una parentesi aperta, ovvero (non null ... Solo per rendere la vita più interessante, ne sono sicuro.
Elise van Looij

Come faccio a rendere un id <> non null? Sento che questa risposta contiene molte conoscenze ma manca di chiarezza.
Fizzybear,

1
@fizzybear devo ammettere che in genere adottano l'approccio opposto. I puntatori sono miei amici e non ho avuto problemi di puntatore "null" / "zero" sin dai primi anni '90. Vorrei poter far sparire l'intera cosa annullabile / annullabile. Ma al punto, la vera risposta è nelle prime 6 righe della risposta. Ma riguardo alla tua domanda: non qualcosa che farei (nessuna critica), quindi non lo so.
William Cerniuk,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.