web-dev-qa-db-ja.com

Objective-Cのnullable、__ nullable、および_Nullableの違い

Xcode 6.3では、Objective-CでAPIの意図をよりよく表現するために新しい注釈が導入されました(そして、Swiftのサポートを改善するためにもちろん)。これらの注釈は、もちろんnonnullnullable、およびnull_unspecifiedでした。

しかし、Xcode 7では、次のような多くの警告が表示されます。

ポインターにnullability型指定子(_Nonnull、_Nullable、または_Null_unspecified)がありません。

それに加えて、Appleは別のタイプのヌル値指定子を使用して、Cコードをマークします( source ):

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

要約すると、これらの3つの異なるNULL可能性アノテーションがあります。

  • nonnullnullablenull_unspecified
  • _Nonnull_Nullable_Null_unspecified
  • __nonnull__nullable__null_unspecified

どの注釈を使用する理由と場所を知っていても、どの種類の注釈を使用するか、どこで、なぜ使用するかについて少し混乱しています。これは私が集めることができるものです:

  • プロパティには、nonnullnullablenull_unspecifiedを使用する必要があります。
  • メソッドパラメータには、nonnullnullablenull_unspecifiedを使用する必要があります。
  • Cメソッドの場合は、__nonnull__nullable__null_unspecifiedを使用する必要があります。
  • ダブルポインターなどの他の場合は、_Nonnull_Nullable_Null_unspecifiedを使用する必要があります。

しかし、基本的に同じことを行う非常に多くの注釈がある理由について、私はまだ混乱しています。

だから私の質問は:

それらの注釈の正確な違いは何ですか、それらを正しく配置する方法と理由は何ですか?

135
Legoless

clangから ドキュメント

Nullability(type)修飾子は、指定されたポインター型の値がnullになる可能性があるか(_Nullable修飾子)、nullに定義された意味がないか(_Nonnull修飾子)、またはnullの目的が不明であるか( _Null_unspecified修飾子)。 nullability修飾子は型システム内で表現されるため、nonnullおよびreturns_nonnull属性よりも一般的であり、null不可能なポインターの配列へのNULL可能ポインターを表現できます。 Nullability修飾子は、適用先のポインターの右側に書き込まれます。

、そして

Objective-Cには、コンテキスト依存のアンダースコアなしのキーワードを使用して、Objective-Cのメソッドとプロパティで使用できるnullability修飾子の代替スペルがあります

そのため、メソッドの戻り値とパラメーターには、単一の下線付きの代わりに、または下線なしの代わりに、二重下線付きバージョン__nonnull/__nullable/__null_unspecifiedを使用できます。 違いは、シングルとダブルの下線付きのものは型定義の後に配置する必要があるが、非下線付きのものは型定義の前に配置する必要があることです

したがって、次の宣言は同等であり、正しいものです。

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

パラメーターの場合:

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

プロパティの場合:

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

ただし、アンダースコア以外のものは許可されないため、ダブルポインターまたはvoid以外の何かを返すブロックが含まれる場合、事態は複雑になります。

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

ブロックをパラメーターとして受け入れるメソッドと同様に、nonnull/nullable修飾子が戻り値ではなくブロックに適用されることに注意してください。したがって、以下は同等です。

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

ブロックに戻り値がある場合、下線バージョンのいずれかに強制されます。

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

結論として、コンパイラーが修飾子を割り当てるアイテムを決定できる限り、いずれかを使用できます。

132
Cristik

Swiftブログ から:

この機能は、キーワード__nullableおよび__nonnullを使用してXcode 6.3で最初にリリースされました。サードパーティのライブラリとの競合の可能性があるため、Xcode 7ではこれらを_Nullableおよび_Nonnullに変更しました。ただし、Xcode 6.3との互換性のために、__ nullableおよび__nonnullのマクロを事前定義して、新しい名前に展開しています。

25
Ben Thomas

私は本当にこの記事が好きだったので、著者が書いたことを単に示しています: https:// swiftunboxed .com/interop/objc-nullability-annotations /

  • null_unspecified:は、Swift暗黙的にアンラップされたオプションにブリッジします。これはdefaultです。
  • nonnull:値はnilになりません。通常の参照への橋渡し。
  • nullable:値はnilにできます。オプションへのブリッジ。
  • null_resettable:読み取り時に値がnilになることはありませんが、nilに設定してリセットすることができます。プロパティにのみ適用されます。

上記の表記は、プロパティまたは関数/変数のcontextで使用するかどうかによって異なります。

Pointers vs. Properties notation

記事の著者はまたニースの例を提供しました:

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

とても便利です

NS_ASSUME_NONNULL_BEGIN 

そして閉じる

NS_ASSUME_NONNULL_END 

これは、特に注記がない限り、everythingがnull以外(またはnonnullまたは_nonnullまたは__nonnull)であると仮定するのが合理的であるため、コードレベル 'nullibis'の必要性を無効にします。

残念ながら、これにも例外があります...

  • typedefsは__nonnullであるとは見なされません(注、nonnullは機能しないようです。使用する必要があるのはugいハーフブラザーです)
  • id *は明示的なnullibiを必要としますが、sin-taxを驚かせます(_Nullable id * _Nonnull <-意味を推測します...)
  • NSError **は常にNULL可能と想定されます

したがって、例外の例外と同じ機能を引き出す一貫性のないキーワードでは、おそらくアプローチはapproachいバージョン__nonnull/__nullable/__null_unspecifiedを使用し、コンパイラーが不平を言ったときに交換することです...?おそらくそれがAppleヘッダーに存在するのでしょうか?

興味深いことに、何かが私のコードにそれを入れました...私はコードでアンダースコアを嫌います(古い学校Apple C++スタイルの男)。 :

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

さらに興味深いことに、__ nullableを挿入した場所が間違っています...(eek @!)

私は本当に非アンダースコアバージョンを使用できればいいのにと思いますが、明らかにこれはエラーとしてフラグが付けられているため、コンパイラでは飛ぶことはありません:

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