使用Xcode 6.3引入了新的注释,以便更好地表达API在 Objective-C 中的意图(并确保更好的Swift支持)。这些注释当然是nonnull
,nullable
和null_unspecified
。
但是对于Xcode 7,会出现很多警告,例如:
指针缺少可为空类型说明符(_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种不同的可空性注释:
nonnull
,nullable
,null_unspecified
_Nonnull
,_Nullable
,_Null_unspecified
__nonnull
,__nullable
,__null_unspecified
即使我知道为什么以及在哪里使用哪个注释,我也会因为我应该使用哪种注释,在哪里以及为什么而感到有些困惑。这是我可以收集的内容:
nonnull
,nullable
,null_unspecified
。nonnull
,nullable
,null_unspecified
。__nonnull
,__nullable
,__null_unspecified
。_Nonnull
,_Nullable
,_Null_unspecified
。但我仍然感到困惑的是,为什么我们有这么多注释基本上做同样的事情。
所以我的问题是:
这些注释之间的确切区别是什么,如何正确放置它们以及为什么?
答案 0 :(得分:132)
来自clang
documentation:
nullability(type)限定符表示给定指针类型的值是否为null(
_Nullable
限定符),没有null的定义含义(_Nonnull
限定符),或者null的目的不明确(_Null_unspecified
限定符)。因为可空性限定符在类型系统中表示,所以它们比nonnull
和returns_nonnull
属性更通用,允许表达(例如)可空指针到非空指针数组。可空性限定符被写在它们应用的指针的右侧。
和
在Objective-C中,可以使用上下文相关的非强制关键字在Objective-C方法和属性中使用可空性限定符的替代拼写
因此,对于方法返回和参数,您可以使用
双重下划线版本__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
作为结论,您可以使用任何一个,只要编译器可以确定要为其分配限定符的项目。
答案 1 :(得分:25)
此功能首先在Xcode 6.3中使用关键字发布 __nullable和__nonnull。由于与第三方库的潜在冲突,我们已将它们在Xcode 7中更改为_Nullable和_Nonnull 你在这看到但是,为了与Xcode 6.3兼容,我们已经 预定义的宏__nullable和__nonnull以扩展为新名称。
答案 2 :(得分:22)
我真的很喜欢 this article ,所以我只是在展示作者写的内容: https://swiftunboxed.com/interop/objc-nullability-annotations/
.SD
桥接到Swift隐式解包的可选项。这是默认。library(data.table)
d1 <- as.data.table(x)
d2 <- data.table(subject=seq_along(b), b)
setkey(d1, subject)
sekey(d2, subject)
d1[d2, head(.SD,b) , by = .EACHI]
:价值不会是零;桥接到常规参考。null_unspecified:
:值可以为零;桥接到可选。nonnull
:读取时该值永远不会为nil,但您可以将其设置为nil以重置它。仅适用于属性。上面的符号,无论您是在属性或函数/变量的上下文中使用它们,都会有所不同:
该文章的作者也提供了一个很好的例子:
nullable
答案 3 :(得分:11)
非常方便
NS_ASSUME_NONNULL_BEGIN
并以
结束NS_ASSUME_NONNULL_END
这将使代码级别'nullibis'的需要无效:-)因为假设一切非空(或nonnull
或{{1}是有意义的除非另有说明,否则}}或_nonnull
。
不幸的是,这也有例外......
__nonnull
s不被认为是typedef
(注意,__nonnull
似乎不起作用,必须使用它的丑陋的同父异母兄弟)nonnull
需要一个明确的无效,但哇sin((id *
&lt; - 猜猜这意味着......)_Nullable id * _Nonnull
始终假定为可空因此,除了异常和引发相同功能的不一致关键字之外,可能的方法是使用丑陋的版本NSError **
/ __nonnull
/ __nullable
并在编译器时进行交换抱怨......?也许这就是为什么它们存在于Apple标题中?
有趣的是,有些东西把它放到我的代码中...我厌恶代码中的下划线(旧式Apple C ++风格的人)所以我绝对相信我没有输入这些但是它们出现了(几个例子):
__null_unspecified
更有趣的是,插入__nullable的地方错了......(eek @!)
我真的希望我可以使用非下划线版本,但显然不会与编译器一起使用,因为这被标记为错误:
typedef void ( ^ DidReceiveChallengeBlock ) ( NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential * __nullable credential );