Objective-C 中 nullable、__nullable 和 _Nullable 的区别

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32452889/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 21:35:37  来源:igfitidea点击:

Difference between nullable, __nullable and _Nullable in Objective-C

objective-cnullableobjective-c-nullability

提问by Legoless

With Xcode 6.3 there were new annotations introduced for better expressing the intention of API's in Objective-C(and to ensure better Swift support of course). Those annotations were of course nonnull, nullableand null_unspecified.

在 Xcode 6.3 中引入了新的注解,以便更好地表达Objective-C中 API 的意图(当然也是为了确保更好的 Swift 支持)。这些注释当然是nonnull,nullablenull_unspecified

But with Xcode 7, there is a lot of warnings appearing such as:

但是使用 Xcode 7,会出现很多警告,例如:

Pointer is missing a nullability type specifier (_Nonnull, _Nullable or _Null_unspecified).

指针缺少可空性类型说明符(_Nonnull、_Nullable 或 _Null_unspecified)。

In addition to that, Apple uses another type of nullability specifiers, marking their C code (source):

除此之外,Apple 使用另一种类型的可空性说明符,标记它们的 C 代码(源代码):

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

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

So, to sum up, we now have these 3 different nullability annotations:

所以,总而言之,我们现在有这 3 个不同的可空性注释:

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

Even though I know why and where to use which annotation, I'm getting slightly confused by which type of annotations should I use, where and why. This is what I could gather:

尽管我知道为什么以及在哪里使用哪个注释,但我还是对应该使用哪种类型的注释、在哪里以及为什么使用感到有些困惑。这是我可以收集的:

  • For properties I should use nonnull, nullable, null_unspecified.
  • For method parameters I should use nonnull, nullable, null_unspecified.
  • For C methods I should use __nonnull, __nullable, __null_unspecified.
  • For other cases, such as double pointers I should use _Nonnull, _Nullable, _Null_unspecified.
  • 对于属性,我应该使用nonnull, nullable, null_unspecified
  • 对于方法参数,我应该使用nonnull, nullable, null_unspecified
  • 对于 C 方法,我应该使用__nonnull, __nullable, __null_unspecified
  • 对于其他情况,例如双指针,我应该使用_Nonnull, _Nullable, _Null_unspecified

But I'm still confused as to why we have so many annotations that basically do the same thing.

但是我仍然不明白为什么我们有这么多基本上做同样事情的注释。

So my question is:

所以我的问题是:

What is exact difference between those annotations, how to correctly place them and why?

这些注释之间的确切区别是什么,如何正确放置它们以及为什么?

回答by Cristik

From the clangdocumentation:

clang文档

The nullability (type) qualifiers express whether a value of a given pointer type can be null (the _Nullablequalifier), doesn't have a defined meaning for null (the _Nonnullqualifier), or for which the purpose of null is unclear (the _Null_unspecifiedqualifier). Because nullability qualifiers are expressed within the type system, they are more general than the nonnulland returns_nonnullattributes, allowing one to express (for example) a nullable pointer to an array of nonnull pointers. Nullability qualifiers are written to the right of the pointer to which they apply.

可空性(类型)限定符表示给定指针类型的值是否可以为空(_Nullable限定符),对于 null(_Nonnull限定符)没有定义的含义,或者 null 的目的不明确(_Null_unspecified限定符) . 因为可空性限定符是在类型系统中表达的,所以它们比nonnullreturns_nonnull属性更通用,允许表达(例如)指向非空指针数组的可为空指针。可空性限定符写在它们所应用的指针的右侧。

, and

, 和

In Objective-C, there is an alternate spelling for the nullability qualifiers that can be used in Objective-C methods and properties using context-sensitive, non-underscored keywords

在 Objective-C 中,可空性限定符有另一种拼写,可以在使用上下文相关的非下划线关键字的 Objective-C 方法和属性中使用

So for method returns and parameters you can use the the double-underscored versions __nonnull/__nullable/__null_unspecifiedinstead of either the single-underscored ones, or instead of the non-underscored ones. The difference is that the single and double underscored ones need to be placed after the type definition, while the non-underscored ones need to be placed before the type definition.

因此,对于方法的返回值和参数,你可以使用双下划线的版本__nonnull/ __nullable/__null_unspecified的,而不是无论是单下划线的,或代替非下划线的。区别在于单双下划线的需要放在类型定义之后,非下划线的需要放在类型定义之前。

Thus, the following declarations are equivalent and are correct:

因此,以下声明是等效的并且是正确的:

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

For parameters:

对于参数:

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

For properties:

对于属性:

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

Things however complicate when double pointers or blocks returning something different than void are involved, as the non-underscore ones are not allowed here:

然而,当涉及双指针或返回与 void 不同的东西的块时,事情会变得复杂,因为这里不允许使用非下划线:

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

Similar with methods that accept blocks as parameters, please note that the nonnull/nullablequalifier applies to the block, and not its return type, thus the following are equivalent:

与接受块作为参数的方法类似,请注意nonnull/nullable限定符适用于块,而不是其返回类型,因此以下是等效的:

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

If the block has a return value, then you're forced into one of the underscore versions:

如果块有返回值,那么您将被迫使用下划线版本之一:

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

As conclusion, you can use either ones, as long as the compiler can determine the item to assign the qualifier to.

作为结论,您可以使用任何一种,只要编译器可以确定将限定符分配给的项目。

回答by Ben Thomas

From the Swift blog:

来自Swift 博客

This feature was first released in Xcode 6.3 with the keywords __nullable and __nonnull. Due to potential conflicts with third-party libraries, we've changed them in Xcode 7 to the _Nullable and _Nonnull you see here. However, for compatibility with Xcode 6.3 we've predefined macros __nullable and __nonnull to expand to the new names.

此功能最初是在 Xcode 6.3 中使用关键字 __nullable 和 __nonnull 发布的。由于与第三方库的潜在冲突,我们已在 Xcode 7 中将它们更改为您在此处看到的 _Nullable 和 _Nonnull。但是,为了与 Xcode 6.3 兼容,我们预定义了宏 __nullable 和 __nonnull 以扩展到新名称。

回答by kgaidis

I really liked this article, so I am merely showing what the author wrote: https://swiftunboxed.com/interop/objc-nullability-annotations/

我真的很喜欢这篇文章,所以我只是展示了作者写的内容:https: //swiftunboxed.com/interop/objc-nullability-annotations/

  • null_unspecified:bridges to a Swift implicitly-unwrapped optional. This is the default.
  • nonnull: the value won't be nil; bridges to a regular reference.
  • nullable: the value can be nil; bridges to an optional.
  • null_resettable: the value can never be nil when read, but you can set it to nil to reset it. Applies to properties only.
  • null_unspecified:连接到 Swift 隐式解包的可选项。这是默认值
  • nonnull: 值不会为零;连接到常规参考。
  • nullable: 值可以为零;桥梁到一个可选的。
  • null_resettable: 该值在读取时永远不能为 nil,但您可以将其设置为 nil 以重置它。仅适用于属性。

The notations above, then differ whether you use them in the contextof properties or functions/variables:

上面的符号,然后在属性或函数/变量的上下文中使用它们时有所不同:

Pointers vs. Properties notation

指针与属性符号

The author of the article also provided a nice example:

文章作者还提供了一个很好的例子:

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

回答by William Cerniuk

Very handy is

非常方便的是

NS_ASSUME_NONNULL_BEGIN 

and closing with

并以

NS_ASSUME_NONNULL_END 

This will nullify the need for the code level 'nullibis' :-) as it sort of makes sense to assume that everythingis non-null (or nonnullor _nonnullor __nonnull) unless otherwise noted.

这将取消对代码级别 'nullibis' :-) 的需求,因为除非另有说明,否则假设一切都是非空的(或nonnull_nonnull__nonnull)是有道理的。

Unfortunately there are exceptions to this as well...

不幸的是,这也有例外......

  • typedefs are not assumed to be __nonnull(note, nonnulldoes not seem to work, have to use it's ugly half brother)
  • id *needs an explicit nullibi but wow the sin-tax ( _Nullable id * _Nonnull<- guess what that means...)
  • NSError **is always assumed nullable
  • typedefs 不被假定为__nonnull(注意,nonnull似乎不起作用,必须使用它的丑陋的同父异母兄弟)
  • id *需要一个明确的无效但哇罪恶税(_Nullable id * _Nonnull<-猜猜这意味着什么......)
  • NSError **始终假定为可空

So with the exceptions to the exceptions and the inconsistent keywords eliciting the same functionality, perhaps the approach is to use the ugly versions __nonnull/ __nullable/ __null_unspecifiedand swap when the complier complains... ? Maybe that is why they exist in the Apple headers?

因此,与例外的例外和不一致的关键字引发相同的功能,也许是方法是使用丑陋的版本__nonnull/ __nullable/__null_unspecified时,编译器抱怨和交换......?也许这就是它们存在于 Apple 标头中的原因?

Interestingly enough, something put it into my code... I abhor underscores in code (old school Apple C++ style guy) so I am absolutely sure I did not type these but they appeared (one example of several):

有趣的是,有些东西将它放入我的代码中......我讨厌代码中的下划线(老派 Apple C++ 风格的家伙)所以我绝对确定我没有输入这些但它们出现了(几个例子中的一个):

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

And even more interestingly, where it inserted the __nullable is wrong... (eek@!)

更有趣的是,它插入 __nullable 的地方是错误的......(eek@!)

I really wish I could just use the non-underscore version but apparently that does not fly with the compiler as this is flagged as an error:

我真的希望我可以只使用非下划线版本,但显然这不会与编译器一起使用,因为这被标记为错误:

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