wake-up-neo.com

Unterschied zwischen nullable, __nullable und _Nullable in Objective-C

Mit Xcode 6.3 wurden neue Annotationen eingeführt, um die Absicht der APIs in Objective-C besser auszudrücken und eine bessere Swift -Unterstützung sicherzustellen Na sicher). Diese Anmerkungen waren natürlich nonnull, nullable und null_unspecified.

Bei Xcode 7 werden jedoch zahlreiche Warnungen angezeigt, z. B .:

Dem Zeiger fehlt ein NULL-Typ-Bezeichner (_Nonnull, _Nullable oder _Null_unspecified).

Darüber hinaus verwendet Apple einen anderen Typ von Nullabilitätsspezifizierern, die ihren C-Code markieren ( source ):

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

Zusammenfassend haben wir nun drei verschiedene Annotationen für die Nullbarkeit:

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

Obwohl ich weiß, warum und wo welche Annotation zu verwenden ist, bin ich etwas verwirrt darüber, welche Art von Annotation ich wo und warum verwenden soll. Das könnte ich sammeln:

  • Für Eigenschaften sollte ich nonnull, nullable, null_unspecified Verwenden.
  • Für Methodenparameter sollte ich nonnull, nullable, null_unspecified Verwenden.
  • Für C-Methoden sollte ich __nonnull, __nullable, __null_unspecified Verwenden.
  • In anderen Fällen, z. B. bei Doppelzeigern, sollte ich _Nonnull, _Nullable, _Null_unspecified Verwenden.

Aber ich bin immer noch verwirrt, warum wir so viele Anmerkungen haben, die im Grunde dasselbe tun.

Meine Frage lautet also:

Was ist der genaue Unterschied zwischen diesen Anmerkungen, wie werden sie richtig platziert und warum?

138
Legoless

Aus der clangDokumentation :

Die Qualifikationsmerkmale nullability (type) drücken aus, ob ein Wert eines bestimmten Zeigertyps null sein kann (das Qualifikationsmerkmal _Nullable), Keine definierte Bedeutung für null hat (das Qualifikationsmerkmal _Nonnull), Oder für die der Zweck von null unklar ist (das Qualifikationsmerkmal _Null_unspecified). Da NULL-Qualifikationsmerkmale innerhalb des Typsystems ausgedrückt werden, sind sie allgemeiner als die Attribute nonnull und returns_nonnull, Sodass beispielsweise ein Zeiger auf ein Array mit nicht leeren Zeigern ausgedrückt werden kann. Nullability-Qualifizierer werden rechts von dem Zeiger geschrieben, auf den sie sich beziehen.

, und

In Objective-C gibt es eine alternative Schreibweise für die NULL-Qualifikationsmerkmale, die in Objective-C-Methoden und -Eigenschaften mit kontextsensitiven, nicht unterstrichenen Schlüsselwörtern verwendet werden können

Für Methodenrückgaben und Parameter können Sie also die doppelt unterstrichenen Versionen __nonnull/__nullable/__null_unspecified Anstelle der einfach unterstrichenen oder der nicht unterstrichenen Versionen verwenden. unterstrichene. Der Unterschied besteht darin, dass die einfach und doppelt unterstrichenen nach der Typdefinition platziert werden müssen, während die nicht unterstrichenen vor der Typdefinition platziert werden müssen.

Daher sind die folgenden Erklärungen gleichwertig und korrekt:

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

Für Parameter:

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

Für Eigenschaften:

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

Kompliziert wird dies jedoch, wenn doppelte Zeiger oder Blöcke beteiligt sind, die etwas anderes als void zurückgeben, da die nicht unterstrichenen hier nicht zulässig sind:

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

Ähnlich wie bei Methoden, die Blöcke als Parameter akzeptieren, beachten Sie, dass das Qualifikationsmerkmal nonnull/nullable für den Block gilt und nicht für seinen Rückgabetyp.

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

Wenn der Block einen Rückgabewert hat, werden Sie zu einer der Unterstreichungsversionen gezwungen:

- (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 Fazit können Sie beide verwenden, solange der Compiler das Element bestimmen kann, dem das Qualifikationsmerkmal zugewiesen werden soll.

137
Cristik

Aus dem Swift Blog :

Diese Funktion wurde erstmals in Xcode 6.3 mit den Schlüsselwörtern __nullable und __nonnull veröffentlicht. Aufgrund möglicher Konflikte mit Bibliotheken von Drittanbietern haben wir sie in Xcode 7 in die hier angezeigten Werte _Nullable und _Nonnull geändert. Aus Gründen der Kompatibilität mit Xcode 6.3 haben wir die Makros __nullable und __nonnull vordefiniert, um sie auf die neuen Namen zu erweitern.

25
Ben Thomas

Ich mochte diesen Artikel, also zeige ich nur, was der Autor geschrieben hat: https: // swiftunboxed .com/interop/objc-nullability-annotations /

  • null_unspecified: Überbrückt eine Swift implizit entpackte Option. Dies ist die Standardeinstellung .
  • nonnull: Der Wert wird nicht null sein. Brücken zu einer regelmäßigen Referenz.
  • nullable: der Wert kann null sein; Brücken zu einem optionalen.
  • null_resettable: Der Wert kann beim Lesen niemals null sein, aber Sie können ihn auf null setzen, um ihn zurückzusetzen. Gilt nur für Eigenschaften.

Die obigen Notationen unterscheiden sich dann, ob Sie sie im Kontext von Eigenschaften oder Funktionen/Variablen verwenden:

Pointers vs. Properties notation

Der Autor des Artikels lieferte auch ein schönes Beispiel:

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

Sehr praktisch ist

NS_ASSUME_NONNULL_BEGIN 

und schließen mit

NS_ASSUME_NONNULL_END 

Dadurch wird die Notwendigkeit der Code-Ebene 'nullibis' aufgehoben, da es irgendwie sinnvoll ist anzunehmen, dass alles nicht null ist (oder nonnull oder _nonnull oder __nonnull) sofern nicht anders angegeben.

Leider gibt es auch hier Ausnahmen ...

  • typedefs werden nicht als __nonnull angenommen (Hinweis, nonnull scheint nicht zu funktionieren, muss seinen hässlichen Halbbruder benutzen)
  • id * Benötigt ein explizites Nullibi, aber wow die Sündensteuer (_Nullable id * _Nonnull <- rate was das bedeutet ...)
  • NSError ** Wird immer als nullbar angenommen

Mit den Ausnahmen zu den Ausnahmen und den inkonsistenten Schlüsselwörtern, die dieselbe Funktionalität hervorrufen, besteht der Ansatz möglicherweise darin, die hässlichen Versionen __nonnull/__nullable/__null_unspecified Zu verwenden und zu tauschen, wenn der Complier beschwert sich ...? Vielleicht sind sie deshalb in den Kopfzeilen von Apple?

Interessanterweise hat es etwas in meinen Code eingefügt ... Ich verabscheue Unterstriche im Code (alter Typ Apple C++)), daher bin ich absolut sicher, dass ich diese nicht eingegeben habe, aber sie erschienen sind (ein Beispiel) von mehreren):

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

Und was noch interessanter ist, wo es eingefügt ist, ist das __nullable falsch ... (eek @!)

Ich wünschte wirklich, ich könnte nur die Nicht-Unterstrich-Version verwenden, aber anscheinend fliegt das nicht mit dem Compiler, da dies als Fehler gekennzeichnet ist:

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