Objective-C'de nullable, __nullable ve _Nullable arasındaki fark


154

Xcode 6.3 ile API'lerin Objective-C'deki niyetini daha iyi ifade etmek (ve elbette daha iyi Swift desteği sağlamak için) için yeni ek açıklamalar getirildi . Bu açıklamalar elbette vardı nonnull, nullableve null_unspecified.

Ancak Xcode 7 ile aşağıdakiler gibi birçok uyarı ortaya çıkıyor:

İşaretçinin bir sıfırlanabilirlik türü belirticisi eksik (_Nonnull, _Nullable veya _Null_unspecified).

Buna ek olarak, Apple, C kodlarını ( kaynak ) işaretleyen başka bir tür nullabilite belirteci kullanır :

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

Özetle, şu 3 farklı sıfırlanabilirlik notuna sahibiz:

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

Hangi ek açıklamayı neden ve nerede kullanacağımı bilsem de, hangi ek açıklamaları kullanmam gerektiğini, nerede ve neden biraz kafam karışıyor. Toplayabileceğim şey bu:

  • Kullanmalıyım özellikleri için nonnull, nullable, null_unspecified.
  • Kullanmalıyım yöntem parametreler için nonnull, nullable, null_unspecified.
  • Kullanmalıyım C yöntemleri için __nonnull, __nullable, __null_unspecified.
  • Böyle kullanmalıyım çift işaretçiler gibi diğer durumlarda, için _Nonnull, _Nullable, _Null_unspecified.

Ama neden aynı şeyi yapan çok fazla ek açıklamaya sahip olduğumuz konusunda hala kafam karıştı.

Benim sorum şu:

Bu ek açıklamalar arasındaki kesin fark nedir, nasıl doğru yerleştirilir ve neden?


3
Bu yazıyı okudum, ancak farkı ve neden şimdi 3 farklı ek notumuz olduğunu açıklamıyor ve neden üçüncü türü eklemeye devam ettiklerini anlamak istiyorum.
Legoless

2
Bu gerçekten Cy-4AH'a yardımcı olmaz ve bunu biliyorsunuz. :)
Legoless

@Legoless, dikkatlice okuduğunuzdan emin misiniz? nerede ve nasıl kullanmanız gerektiğini, denetlenen kapsamları neler olduğunu, diğerini daha iyi okunabilirlik, uyumluluk nedenleri vb. için kullanabileceğinizi açıklar ... gerçekten ne sormak istediğinizi bilmiyor olabilirsiniz, ancak cevap açıkça bağlantı altında. belki sadece benim, ama amaçlarını açıklamak, bu basit açıklamayı buraya bir cevap olarak kopyalamak ve yapıştırmak için daha fazla açıklamanın gerekli olacağını düşünmüyorum sanırım. :(
holex

2
Bazı bölümleri temizliyor, ama hayır, neden sadece ilk ek açıklamalara sahip olmadığımızı anlamıyorum. Sadece __nullable'dan _Nullable'a neden gittiklerini açıklar, ancak nullable'ımız varsa neden _Nullable'a bile ihtiyacımız yok. Ayrıca Apple'ın neden hala kendi kodlarında __nullable kullandığını açıklamıyor.
Legoless

Yanıtlar:


152

Gönderen clang belgeler :

Sıfırlanabilirlik (tür) niteleyicileri, belirli bir işaretçi türü değerinin null ( _Nullableniteleyici) olup olmadığını, null ( niteleyici) için tanımlanmış bir anlama sahip olmadığını _Nonnullveya null değerinin amacının net olmadığını ( _Null_unspecifiedniteleyici) ifade eder. . Nullabilite niteleyicileri tür sistemi içinde ifade edildiğinden, bunlar nonnullve returns_nonnullözniteliklerinden daha geneldir , birisinin boş olmayan bir işaretçi dizisine boş bir işaretçi ifade etmesine (örneğin) izin verir. Güvenlik açığı niteleyicileri, uygulandıkları işaretçinin sağına yazılır.

, ve

Objective-C'de, bağlam duyarlı, alt çizgi olmayan anahtar kelimeler kullanılarak Objective-C yöntemlerinde ve özelliklerinde kullanılabilen nullabilite niteleyicileri için alternatif bir yazım denetimi vardır

Bu nedenle, yöntem döndürme ve parametreleri için, altı çizili sürümleri __nonnull/ veya altı çizili olmayanları yerine çift ​​altı çizili sürümleri / __nullable/ kullanabilirsiniz __null_unspecified. Aradaki fark, tek ve çift alt çizgi olanların tür tanımından sonra yerleştirilmesi gerektiğidir, alt çizgi olmayanların ise tür tanımından önce yerleştirilmesi gerekir.

Bu nedenle, aşağıdaki beyanlar eşdeğerdir ve doğrudur:

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

Parametreler için:

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

Mülkler için:

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

Bununla birlikte, burada altı çizili olanlara izin verilmediğinden, boşluktan farklı bir şey döndüren çift işaretçiler veya bloklar söz konusu olduğunda işler karmaşıklaşır:

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

Blokları parametre olarak kabul eden yöntemlere benzer şekilde, nonnull/ nullablequalifier öğesinin dönüş türü için değil, blok için geçerli olduğunu ve dolayısıyla aşağıdakilerin eşdeğer olduğunu lütfen unutmayın :

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

Bloğun dönüş değeri varsa, alt çizgi sürümlerinden birine zorlanırsınız:

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

Sonuç olarak, derleyici niteleyiciyi atayacak öğeyi belirleyebildiği sürece her ikisini de kullanabilirsiniz.


2
Alt çizgi sürümlerinin her yerde kullanılabileceği anlaşılıyor, bu yüzden bazı yerlerde altçizilmiş sürümleri ve başkalarında altçizgi olmayan sürümleri kullanmak yerine bunları sürekli olarak kullanacağımı düşünüyorum. Doğru?
Vaddadi Kartick

@KartickVaddadi evet, doğru, sürekli olarak tek altı çizili veya çift altı çizili sürümleri kullanabilirsiniz.
Cristik

_Null_unspecifiedSwift içinde bu isteğe bağlı anlamına gelir? isteğe bağlı olmayan veya ne?
Tatlım

1
@Honey _Null_unspecified, Swift'e Örtük Olarak
Açılmamış

1
@Cristik aha. Ben ne zaman çünkü ... varsayılan değer olduğunu tahmin etmedi ben Çizelgesi opsiyonel ... Dolaylı olarak başlamıştı belirtmek
Bal

28

Gönderen Swift blogda :

Bu özellik ilk olarak Xcode 6.3'te __nullable ve __nonnull anahtar sözcükleriyle piyasaya sürüldü. Üçüncü taraf kitaplıklarıyla olası çakışmalar nedeniyle, Xcode 7'de bunları burada gördüğünüz _Nullable ve _Nonnull olarak değiştirdik. Bununla birlikte, Xcode 6.3 ile uyumluluk için __nullable ve __nonnull makrolarını yeni adlara genişletmek üzere önceden tanımladık.


4
Özetle, tek ve çift alt çizgi versiyonları aynıdır.
Vaddadi Kartick

4
Ayrıca Xcode 7.0 sürüm notlarında da belgelenmiştir: "Çift altı çizili sıfırlanabilirlik niteleyicileri (__nullable, __nonnull ve __null_unspecified), sırasıyla büyük harfli tek bir alt çizgi kullanacak şekilde yeniden adlandırılmıştır: Derleyici makroları önceden tanımlamaktadır. eski çift belirtilmemiş adlardan kaynak uyumluluğu için yeni adlara eşleme. (21530726) "
Cosyn

25

Bu makaleyi gerçekten beğendim , bu yüzden sadece yazarın yazdıklarını gösteriyorum: https://swiftunboxed.com/interop/objc-nullability-annotations/

  • null_unspecified:Swift ile örtülü olarak açılmamış isteğe bağlı bir köprü oluşturur. Bu varsayılan ayardır .
  • nonnull: değer sıfır olmayacak; düzenli bir referans köprüler.
  • nullable: değer sıfır olabilir; isteğe bağlı köprüler.
  • null_resettable: değer okunduğunda asla sıfır olamaz, ancak sıfırlamak için sıfır olarak ayarlayabilirsiniz. Yalnızca mülkler için geçerlidir.

Yukarıdaki gösterimler, bunları özellikler veya işlevler / değişkenler bağlamında kullanıp kullanmadığınıza göre farklılık gösterir :

İşaretçiler ve Özellikler gösterimi

Makalenin yazarı da güzel bir örnek verdi:

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

Çok kullanışlı

NS_ASSUME_NONNULL_BEGIN 

ve ile kapanıyor

NS_ASSUME_NONNULL_END 

Bu, aksi belirtilmedikçe her şeyin null (veya nonnullveya _nonnullveya __nonnull) olmadığını varsaymak mantıklı olduğundan, 'nullibis' kod seviyesi :-) ihtiyacını ortadan kaldıracaktır .

Ne yazık ki bunun da istisnaları var ...

  • typedefs olarak kabul edilmez __nonnull(not, nonnullişe yaramaz gibi görünüyor, çirkin üvey kardeşini kullanmak zorunda)
  • id *açık bir nullibi gerekiyor ama günah vergisini vay ( _Nullable id * _Nonnull<- bunun ne anlama geldiğini tahmin et ...)
  • NSError ** her zaman sıfırlanabilir kabul edilir

Yani istisnalar ve aynı işlevselliği ortaya çıkaran tutarsız anahtar kelimelerin istisnaları ile, belki de yaklaşım, şikayetçi çirkin sürümleri __nonnull/ __nullable/ __null_unspecifiedve takas kullanmaktır ...? Belki de Apple başlıklarında var olmasının nedeni budur?

İlginç bir şekilde, bir şey benim kod koymak ... Ben kod (eski okul Apple C ++ tarzı adam) alt çizgileri abhor bu yüzden kesinlikle bu yazmadım eminim ama onlar (birkaç örnek) ortaya çıktı:

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

Ve daha da ilginç bir şekilde, __nullable'ı yerleştirdiği yer yanlış ... (eek @!)

Gerçekten ben sadece alt çizgi olmayan sürümünü kullanabilirsiniz ama görünüşe göre bu bir hata olarak işaretlenir gibi derleyici ile uçmaz:

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

2
Alt çizgi olmayanlar sadece bir açılış parantezinden sonra doğrudan kullanılabilir, yani (boş olmayan ... Sadece hayatı daha ilginç hale getirmek için, eminim.
Elise van Looij

Null olmayan bir kimliği nasıl oluştururum? Bu cevabın çok fazla bilgi içerdiğini hissediyorum ama netliği yok.
fizzybear

1
@fizzybear itiraf ediyorum ben genellikle tam tersi bir yaklaşım. İşaretçiler arkadaşım ve 90'ların başından beri "null" / "nil" işaretçi sorunları yoktu. Keşke bütün boş / hüzünlü şeyleri gidebilseydim. Ancak, asıl cevap, cevap cevabının ilk 6 satırındadır. Ama sorunuz hakkında: Yapacağım bir şey değil (eleştiri yok) bu yüzden bilmiyorum.
William Cerniuk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.