在Objective-C中可为空,__ nullable和_Nullable之间的区别

在Objective-C中可为空,__ nullable和_Nullable之间的区别

问题描述:

在Xcode 6.3中引入了新的注释,以更好地表达 Objective-C 中API的意图(当然,并确保更好的Swift支持).这些注释当然是nonnullnullablenull_unspecified.

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, nullable and null_unspecified.

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

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

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

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

除此之外,Apple使用另一种可为空的说明符,标记其C代码(

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

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

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

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

  • nonnullnullablenull_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:

  • 对于属性,我应该使用nonnullnullablenull_unspecified.
  • 对于方法参数,我应该使用nonnullnullablenull_unspecified.
  • 对于C方法,我应该使用__nonnull__nullable__null_unspecified.
  • 对于其他情况,例如双指针,我应该使用_Nonnull_Nullable_Null_unspecified.
  • 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.

但是对于为什么我们有这么多基本上执行相同操作的注释,我仍然感到困惑.

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

所以我的问题是:

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

来自clang 文档:

可空性(类型)限定符表示给定指针类型的值是否可以为空(_Nullable限定符),没有为null定义的含义(_Nonnull限定符)或null的用途尚不清楚(_Null_unspecified限定符).由于可空性限定符在类型系统中表示,因此它们比nonnullreturns_nonnull属性更通用,从而允许将(例如)可空指针表示为非空指针数组.可空性限定符写在它们所适用的指针的右侧.

The nullability (type) qualifiers express whether a value of a given pointer type can be null (the _Nullable qualifier), doesn’t have a defined meaning for null (the _Nonnull qualifier), or for which the purpose of null is unclear (the _Null_unspecified qualifier). Because nullability qualifiers are expressed within the type system, they are more general than the nonnull and returns_nonnull attributes, 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.

在Objective-C中,可以使用上下文敏感的非强调关键字在nullable限定词中使用替代拼写,可以在Objective-C方法和属性中使用

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

因此,对于方法返回和参数,您可以使用 带双下划线的版本__nonnull/__nullable/__null_unspecified,而不是单下划线版本或非下划线版本. 区别在于,必须在类型定义之后放置单划线和双下划线,而在类型定义之前放置非下划线.

So for method returns and parameters you can use the the double-underscored versions __nonnull/__nullable/__null_unspecified instead 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.

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

Thus, the following declarations are equivalent and are correct:

- (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不同时,事情就变得复杂了,因为这里不允许使用非下划线:

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)compute:(NSError *  _Nullable * _Nullable)error
- (void)compute:(NSError *  __nullable * _Null_unspecified)error;
// and all other combinations

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

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

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