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

使用Xcode 6.3,为了更好地expressionAPI在Objective-C中的意图(并确保更好的Swift支持),引入了新的注释。 这些注释当然null_unspecified nullablenullablenull_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 nullablenullablenull_unspecified
  • _Nonnull_Nullable_Null_unspecified
  • __nonnull__nullable__null_unspecified

尽pipe我知道为什么以及在哪里使用哪个注释,我对使用哪种types的注释,在哪里以及为什么会有些困惑。 这是我可以收集的:

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

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

所以我的问题是:

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

clang 文件 :

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