Objective-C中nullable,__nullable和_Nullable之间的区别
使用Xcode 6.3,为了更好地expressionAPI在Objective-C中的意图(并确保更好的Swift支持),引入了新的注释。 这些注释当然null_unspecified
nullable
, nullable
, null_unspecified
nullable
null_unspecified
。
但是在Xcode 7中,出现了很多警告,比如:
指针缺less可为空types说明符(_Nonnull,_Nullable或_Null_unspecified)。
除此之外,苹果公司使用另一种types的可空性说明符,标记他们的C代码( 来源 ):
CFArrayRef __nonnull CFArrayCreate( CFAllocatorRef __nullable allocator, const void * __nonnull * __nullable values, CFIndex numValues, const CFArrayCallBacks * __nullable callBacks);
所以,总结一下,我们现在有3个不同的可空性注释:
-
nonnull
nullable
,nullable
,null_unspecified
-
_Nonnull
,_Nullable
,_Null_unspecified
-
__nonnull
,__nullable
,__null_unspecified
尽pipe我知道为什么以及在哪里使用哪个注释,我对使用哪种types的注释,在哪里以及为什么会有些困惑。 这是我可以收集的:
- 对于属性我应该使用
null_unspecified
,nullable
,null_unspecified
。 - 对于方法参数,我应该使用
null_unspecified
,nullable
,null_unspecified
。 - 对于C方法,我应该使用
__nonnull
,__nullable
,__null_unspecified
。 - 对于其他情况,例如双指针,我应该使用
_Nonnull
,_Nullable
,_Null_unspecified
。
但我仍然困惑,为什么我们有这么多的注释基本上做同样的事情。
所以我的问题是:
这些注释之间的确切区别是什么,如何正确放置它们以及为什么?
从clang
文件 :
nullability(type)限定符表示给定指针types的值是否可以为null(
_Nullable
限定符),没有为null定义的含义(_Nonnull
限定符),或者null的目的不明确_Null_unspecified
限定符)。 因为可以在types系统中表示可空性限定符,所以它们比nonnull
和returns_nonnull
属性更普遍,从而允许将可空指针表示为非空指针数组。 可以为空的限定符写在它们所应用的指针的右侧。
,和
在Objective-C中,可以使用上下文敏感的非下划线关键字在Objective-C方法和属性中使用可空性限定符的替代拼写
因此,对于方法返回和参数,您可以使用双下划线版本__nonnull
/ __nullable
/ __null_unspecified
而不是单下划线版本,或者使用非下划线版本。 区别在于单个和双重下划线的需要放在types定义之后,而非下划线的需要放在types定义之前。
因此,以下声明是等同的,是正确的:
- (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
限定符适用于该块,而不是其返回types,因此以下内容是等效的:
- (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
作为结论,只要编译器可以确定要分配限定符的项目,就可以使用其中的一个。
从Swift博客 :
这个特性最初是在Xcode 6.3中用关键字__nullable和__nonnull发布的。 由于与第三方库的潜在冲突,我们已经将它们在Xcode 7中更改为_Nullable和_Nonnull,您在这里看到。 但是,为了与Xcode 6.3兼容,我们使用了预定义macros__nullable和__nonnull来扩展到新名称。
我真的很喜欢这篇文章 ,所以我只是展示了作者写的东西: https : //swiftunboxed.com/interop/objc-nullability-annotations/
-
null_unspecified:
桥接到一个Swift隐式解包可选。 这是默认的 。 -
nonnull
:值不会为零; 桥接到一个常规的参考。 -
nullable
:值可以为零; 桥梁可选。 -
null_resettable
:读取时该值永远不能为零,但可以将其设置为零来重置它。 仅适用于属性。
上面的符号,然后不同你是否在属性或函数/variables的上下文中使用它们:
文章的作者也提供了一个很好的例子:
// 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;
非常好用
NS_ASSUME_NONNULL_BEGIN
并closures
NS_ASSUME_NONNULL_END
这将使代码级别“nullibis”:-)的需求无效,因为除非另有说明,否则假定所有内容都是非空(或非空或_nonnull
或__nonnull
)是__nonnull
。
不幸的是,这也有例外…
-
typedef
不被假定为__nonnull
(注意,__nonnull
似乎不工作,不得不使用它的丑陋的一半兄弟) -
id *
需要一个明确的nullibi但哇罪过税(_Nullable id * _Nonnull
< – 猜测是什么意思…) -
NSError **
始终是可空的
因此,除了例外情况和不一致的关键字引出相同的function,可能的方法是使用丑陋的版本__nonnull
/ __nullable
/ __null_unspecified
和交换当编译器抱怨…? 也许这就是为什么他们存在苹果头?
有趣的是,有些东西把它放到我的代码中…我厌恶代码中的下划线(老派的苹果C ++风格的人),所以我绝对相信我没有input这些东西,但他们出现了(几个例子之一):
typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential );
更有意思的是,在哪里插入__nullable是错误的…(eek @!)
我真的希望我可以只使用非下划线版本,但显然这不会与编译器,因为这是标记为错误:
typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * nonnull credential );