Objective-Cにおけるnullable、__ nullable、_Nullableの違い


154

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

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

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

それに加えて、Appleは別のタイプのnull可能指定子を使用して、Cコード(ソース)をマークします。

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

しかし、基本的に同じことを行うアノテーションがたくさんある理由については、まだ混乱しています。

だから私の質問は:

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


3
私はその投稿を読みましたが、違いや、現在3種類の注釈が​​ある理由については説明していません。なぜ彼らが3番目の注釈を追加したのかを理解したいのです。
Legoless、2015

2
これは本当に@ Cy-4AHを助けません、そしてあなたはそれを知っています。:)
Legoless

@Legoless、あなたはそれを注意深く読んでよろしいですか?それらをどこでどのように使用する必要があるか、監査対象のスコープは何か、読みやすくするために他のスコープを使用できる場合、互換性の理由などを正確に説明します...本当に何を聞きたいのかわからないかもしれませんが、答えは明らかにリンクの下にあります。多分私だけかもしれませんが、彼らの目的を説明するためにこれ以上の説明が必要だとは思わないので、ここに答えとしてここに簡単な説明をコピーして貼り付けるのは本当に厄介だと思います。:(
holex

2
特定の部分をクリアしますが、いいえ、なぜ最初の注釈だけがないのかまだわかりません。それは、なぜそれらが__nullableから_Nullableに移行したのかを説明するだけであり、nullableがある場合に_Nullableが必要な理由さえ説明しません。また、Appleが独自のコードで__nullableを使用する理由も説明していません。
Legoless

回答:


152

clang ドキュメントから:

null可能性(タイプ)修飾子は、特定のポインター型の値がnullである可能性がある(_Nullable修飾子)か、nullに定義された意味がない(_Nonnull修飾子)か、またはnullの目的が不明確であるか(_Null_unspecified修飾子)を表します。nullability修飾子は型システム内で表現されるため、nonnulland returns_nonnull属性よりも一般的であり、(たとえば)null以外のポインターの配列へのnull可能なポインターを表現できます。NULL可能性修飾子は、それらが適用されるポインターの右側に書き込まれます。

、および

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

したがって、メソッドの戻り値とパラメーターについては、シングルアンダースコアのバージョンの代わりに、またはアンダースコアのないバージョンの代わりに、ダブルアンダースコアのバージョン__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

結論として、修飾子を割り当てるアイテムをコンパイラが決定できる限り、どちらを使用してもかまいません。


2
アンダースコアのバージョンはどこでも使用できるようですので、一部の場所でアンダースコアのバージョンを使用し、他のバージョンではアンダースコアのないバージョンを使用するのではなく、一貫して使用することを想定しています。正しい?
Vaddadi Kartick

@KartickVaddadiはい、正解です。シングルアンダースコアまたはダブルアンダースコアのバージョンを一貫して使用できます。
Cristik

_Null_unspecifiedSwiftではこれはオプションに変換されますか?非オプションまたは何ですか?
Honey

1
@Honey _Null_unspecifiedは、暗黙的にアンラップされたオプションとしてSwiftにインポートされます
Cristik

1
@Cristik aha。私はそれがそのデフォルト値だと思います... 指定しなかったとき、暗黙的にアンラップされたオプションを取得していたので...
Honey

28

Swiftブログから:

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


4
簡単に言うと、シングルとダブルのアンダースコア付きバージョンは同じです。
Vaddadi Kartick

4
Xcode 7.0のリリースノートにも記載されています:「二重下線付きのnull可能性修飾子(__nullable、__ nonnull、および__null_unspecified)は、それぞれ大文字の単一下線を使用するように名前が変更されました:_Nullable、_Nonnull、および_Null_unspecified。コンパイラーはマクロを事前定義します。ソースの互換性のために、二重の未指定の古い名前から新しい名前へのマッピング。(21530726) "
Cosyn

25

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

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

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

ポインタとプロパティの表記

記事の作成者も、すばらしい例を提供しました。

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

とても便利です

NS_ASSUME_NONNULL_BEGIN 

そして最後に

NS_ASSUME_NONNULL_END 

これは、特に明記されていない限り、すべてが非null(またはnonnullor _nonnullまたは__nonnull)であると想定するのが理にかなっているため、コードレベル 'nullibis'の必要性を無効にします。

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

  • typedefsは想定されていません__nonnull(注意、nonnull動作しないようです。醜い半兄弟を使用する必要があります)
  • id *明示的なnullibiが必要ですが、sin-taxがすごい_Nullable id * _Nonnullです(<-意味がわかります...)
  • NSError ** 常にヌル可能と見なされます

だから、例外の例外と同じ機能を誘発一貫性のないキーワードで、おそらくアプローチは醜いバージョンを使用することです__nonnull/ __nullable/ __null_unspecifiedとswapコンパイラが文句を言うとき...?たぶんそれがAppleヘッダーに存在する理由でしょうか?

興味深いことに、コードに何かが含まれています...コードのアンダースコア(旧式のApple C ++スタイルの人)は嫌いなので、これらを入力しなかったのは確かですが、表示されました(いくつかの一例):

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

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

私は本当にアンダースコア以外のバージョンを使用できればいいのにと思っていますが、これはエラーとしてフラグが立てられているので、コンパイラではうまくいかないようです:

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

2
アンダースコア以外は、左括弧の直後にのみ使用できます。つまり、(nonnull ...人生をもっと面白くするために、きっと
エリスファンローイ

id <>を非ヌルにするにはどうすればよいですか?この回答には多くの知識が含まれているように感じますが、明確さには欠けています。
fizzybear

1
@fizzybearは確かに私は通常、反対のアプローチをとります。ポインターは私の友人であり、私は90年代の初め以来「null」/「nil」ポインターの問題を抱えていません。nullable / nilable全体をそのままにしておけばよかったのですが。しかし、要は、実際の回答は回答の返信の最初の6行にあります。しかし、あなたの質問について:私がすることではない(批判なし)ので、私にはわかりません。
William Cerniuk
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.