Objective-C中nullable,__nullable和_Nullable之间的区别

时间:2015-09-08 08:29:56

标签: objective-c nullable objective-c-nullability

使用Xcode 6.3引入了新的注释,以便更好地表达API在 Objective-C 中的意图(并确保更好的Swift支持)。这些注释当然是nonnullnullablenull_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种不同的可空性注释:

  • nonnullnullablenull_unspecified
  • _Nonnull_Nullable_Null_unspecified
  • __nonnull__nullable__null_unspecified

即使我知道为什么以及在哪里使用哪个注释,我也会因为我应该使用哪种注释,在哪里以及为什么而感到有些困惑。这是我可以收集的内容:

  • 对于属性,我应该使用nonnullnullablenull_unspecified
  • 对于方法参数,我应该使用nonnullnullablenull_unspecified
  • 对于C方法,我应该使用__nonnull__nullable__null_unspecified
  • 对于其他情况,例如双指针,我应该使用_Nonnull_Nullable_Null_unspecified

但我仍然感到困惑的是,为什么我们有这么多注释基本上做同样的事情。

所以我的问题是:

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

4 个答案:

答案 0 :(得分:132)

来自clang documentation

  

nullability(type)限定符表示给定指针类型的值是否为null(_Nullable限定符),没有null的定义含义(_Nonnull限定符),或者null的目的不明确(_Null_unspecified限定符)。因为可空性限定符在类型系统中表示,所以它们比nonnullreturns_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)

来自the Swift blog

  

此功能首先在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以重置它。仅适用于属性。

上面的符号,无论您是在属性或函数/变量的上下文中使用它们,都会有所不同:

Pointers vs. Properties notation

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

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