Verschil tussen nullable, __nullable en _Nullable in Objective-C

Met Xcode 6.3 zijn er nieuwe annotaties geïntroduceerd om de bedoeling van API’s beter tot uitdrukking te brengen in Objective-C(en om natuurlijk voor betere Swift-ondersteuning te zorgen). Die annotaties waren natuurlijk nonnull, nullableen null_unspecified.

Maar met Xcode 7 verschijnen er veel waarschuwingen zoals:

Aanwijzer mist een specificatie van het type nullabiliteit (_Nonnull, _Nullable of _Null_unspecified).

Daarnaast gebruikt Apple een ander type nullability-specificaties, die hun C-code markeren (bron):

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

Dus, om samen te vatten, we hebben nu deze 3 verschillende nullability-annotaties:

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

Hoewel ik weet waarom en waar ik welke annotatie moet gebruiken, raak ik een beetje in de war over welk type annotatie ik moet gebruiken, waar en waarom. Dit is wat ik kon verzamelen:

  • Voor eigenschappen moet ik nonnull, nullable, null_unspecifiedgebruiken.
  • Voor methodeparameters moet ik nonnull, nullable, null_unspecifiedgebruiken.
  • Voor C-methoden moet ik __nonnull, __nullable, __null_unspecifiedgebruiken.
  • Voor andere gevallen, zoals dubbele verwijzingen, moet ik _nonnull, _Nullable, _Null_unspecifiedgebruiken.

Maar ik weet nog steeds niet waarom we zoveel annotaties hebben die in feite hetzelfde doen.

Dus mijn vraag is:

Wat is het exacte verschil tussen die annotaties, hoe je ze correct plaatst en waarom?


Antwoord 1, autoriteit 100%

Van de clangdocumentatie:

De kwalificaties voor nullabiliteit (type) geven aan of een waarde van een bepaald type aanwijzer null kan zijn (de kwalificatie _Nullable), geen gedefinieerde betekenis heeft voor null (de _nonnullkwalificatie), of waarvoor het doel van null onduidelijk is (de _Null_unspecifiedkwalificatie). Omdat nullability-kwalificaties worden uitgedrukt binnen het typesysteem, zijn ze algemener dan de attributen nonnullen returns_nonnull, waardoor men (bijvoorbeeld) een nullable pointer kan uitdrukken naar een array van niet-null-aanwijzers. Nullability-kwalificaties worden rechts van de aanwijzer waarop ze van toepassing zijn geschreven.

, en

In Objective-C is er een alternatieve spelling voor de nullability-kwalificaties die kunnen worden gebruikt in Objective-C-methoden en -eigenschappen met behulp van contextgevoelige, niet-onderstreepte trefwoorden

Dus voor methode-returns en parameters kun je de . gebruiken
de dubbel onderstreepte versies __nonnull/__nullable/__null_unspecifiedin plaats van de enkel onderstreepte versies, of in plaats van de niet-onderstreepte versies. Het verschil is dat de enkele en dubbele onderstreepte tekens achter de typedefinitie moeten worden geplaatst, terwijl de niet-onderstreepte tekens vóór de typedefinitie moeten worden geplaatst.

De volgende verklaringen zijn dus equivalent en correct:

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

Voor parameters:

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

Voor eigendommen:

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

Het wordt echter ingewikkelder als er dubbele aanwijzers of blokken zijn die iets anders teruggeven dan ongeldig, omdat de niet-onderstrepingstekens hier niet zijn toegestaan:

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

Vergelijkbaar met methoden die blokken als parameters accepteren, houd er rekening mee dat de kwalificatie nonnull/nullablevan toepassing is op het blok, en niet op het retourtype, dus de volgende zijn equivalent :

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

Als het blok een retourwaarde heeft, wordt u gedwongen tot een van de underscore-versies:

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

Als conclusie kun je beide gebruiken, zolang de compiler maar kan bepalen aan welk item de kwalificatie moet worden toegewezen.


Antwoord 2, autoriteit 17%

Van het Swift-blog:

Deze functie werd voor het eerst uitgebracht in Xcode 6.3 met de trefwoorden
__nullable en __nonnull. Vanwege mogelijke conflicten met bibliotheken van derden hebben we ze in Xcode 7 gewijzigd in de _Nullable en _Nonnull
zie je hier. Voor compatibiliteit met Xcode 6.3 hebben we echter
vooraf gedefinieerde macro’s __nullable en __nonnull om uit te breiden naar de nieuwe namen.


Antwoord 3, autoriteit 17%

Van de clang-documentatie:

De kwalificaties voor nullability (type) geven aan of een waarde van een bepaald pointertype null kan zijn.

Meestal gebruikt u nonnullen nullable.

Hieronder staan ​​alle beschikbare specificaties. Uit dit artikel:

  • null_unspecified:Dit is de standaardinstelling.Het vormt een brug naar een impliciet uitgepakte Swift-optie.
  • nonnull: de waarde zal niet nul zijn. Het vormt een brug naar een reguliere Swift-referentie.
  • nullable: de waarde kan nul zijn. Het overbrugt naar een Swift optioneel.
  • null_resettable: de waarde kan bij het lezen nooit nul zijn, maar je kunt hem op nul zetten om hem te resetten. Alleen van toepassing op eigendommen.

De bovenstaande notaties verschillen of je ze gebruikt in de contextvan eigenschappen of functies/variabelen:

Pointers vs. Eigenschappen notatie

De auteur van het artikel gaf ook een mooi voorbeeld:

// 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;

Antwoord 4, autoriteit 8%

Heel handig is

NS_ASSUME_NONNULL_BEGIN 

en afsluiten met

NS_ASSUME_NONNULL_END 

Hierdoor vervalt de noodzaak van het codeniveau ‘nullibis’ 🙂 omdat het logisch is om aan te nemen dat allesniet-null is (of nonnullof _nonnullof __nonnull) tenzij anders vermeld.

Helaas zijn hier ook uitzonderingen op…

  • typedefs worden niet verondersteld __nonnullte zijn (let op, nonnulllijkt niet te werken, moet zijn lelijke halfbroer gebruiken)
  • id *heeft een expliciete nullibi nodig, maar wow de sin-tax ( _Nullable id * _Nonnull<- raad eens wat dat betekent…)
  • NSError **wordt altijd verondersteld nullable te zijn

Dus met de uitzonderingen op de uitzonderingen en de inconsistente trefwoorden die dezelfde functionaliteit oproepen, is de benadering misschien om de lelijke versies __nonnull/ __nullable/ __null_unspecifieden ruilen wanneer de complier klaagt… ? Misschien staan ​​ze daarom in de Apple-headers?

Interessant genoeg heeft iets het in mijn code gestopt… Ik verafschuw onderstrepingstekens in code (old school Apple C++ style guy) dus ik weet absoluut zeker dat ik deze niet heb getypt, maar ze verschenen (een voorbeeld van meerdere):

p>

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

En nog interessanter, waar de __nullable is ingevoegd, is verkeerd… (eek@!)

Ik zou echt willen dat ik de versie zonder onderstrepingsteken kon gebruiken, maar blijkbaar werkt dat niet met de compiler omdat dit wordt gemarkeerd als een fout:

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

Other episodes