在OC中存在__nullable
与之对应的__nonnull
修饰符,苹果为了避免与第三方库潜在的冲突把 __nonnull/__nullable
改成 _Nonnull/_Nullable
。同时苹果同样还支持没有下划线的写法 nonnull/nullable
,这三种写法本质上都是互通的,只是放的位置不同,意义用途如下:
属性 | 表示意义 | 用途 | 区别 |
---|---|---|---|
__nullable 、_Nullable、nullable | 表示对象可以是 NULL 或 nil | 方法返回值修饰、参数修饰、声明属性修饰 | 放置位置不同 |
__nonnull、_Nonnull、nonnull | 表示对象不应该为空 | 同上 | 同上 |
使用示例:
- 方法返回值修饰:
- (nullable NSString *)function; - (NSString * __nullable) function; - (NSString * _Nullable) function;
- 声明属性的修饰:
@property (nonatomic, copy, nullable) NSString * param; @property (nonatomic, copy) NSString * __nullable param; @property (nonatomic, copy) NSString * _Nullable param;
- 方法参数修饰:
- (void)functionWithParam:(nullable NSString *) param; - (void)functionWithParam:(NSString * _Nullable) param; - (void)functionWithParam:(NSString * __nullable) param;
而对于 双指针类型对象
、Block 的返回值
、Block 的参数
等,这时候就不能用 nonnull/nullable
修饰,只能用带下划线的 __nonnull/__nullable
或者 _Nonnull/_Nullable
:
- (void)functionWithError:(NSError * _Nullable * _Nullable)error
- (void)functionWithError:(NSError * __nullable * __null_unspecified)error;
- (void)functionWithBlock:(nullable(整体修饰符位置-外界传入block是否可以为空) id __nonnull(返回修饰符位置-block返回值是否可以为空) (^)(id __nullable(参数修饰符位置) params))block;
// 上面的 nullable 用于修饰方法传入的参数 Block 可以为空,__nonnull 用于修饰 Block 返回值 id 不能为空;
根据原生 iOS SDK 里 Foundation 和 UIKit 的头文件以及苹果的博文《Nullability and Objective-C》,总结如下使用规范:
-
对于属性、方法返回值、方法参数的修饰,使用:
nonnull/nullable
; -
对于 C 函数的参数、Block 的参数、Block 返回值的修饰,使用:
_Nonnull/_Nullable
,建议弃用。__nonnull/__nullable
Nonnull Audited Regions
如果每个属性或每个方法都去指定 nonnull
和 nullable
,将是一件非常繁琐的事。苹果为了减轻我们的工作量,专门提供了两个宏:NS_ASSUME_NONNULL_BEGIN
和 NS_ASSUME_NONNULL_END
。在这两个宏之间的代码,所有简单指针对象都被假定为 nonnull
,因此我们只需要去指定那些 nullable
指针对象即可。如下代码所示:
NS_ASSUME_NONNULL_BEGIN
@interface myClass ()
@property (nonatomic, copy) NSString * param;
- (id)functionWithParam:(nullable NSString *)param;
@end
NS_ASSUME_NONNULL_END
在上面的代码中,param
属性默认是 nonnull
的,functionWithParam:
方法的返回值也是 nonnull
,而方法的参数 str
被显式指定为 nullable
。
为了安全起见,苹果还制定了以下几条规则:
- 通过
typedef
定义的类型的nullability
特性通常依赖于上下文,即使是在 Audited Regions 中,也不能假定它为nonnull
; - 对于复杂的指针类型(如
id *
)必须显式去指定是nonnull
还是nullable
。例如,指定一个指向nullable
对象的nonnull
指针,可以使用__nullable id * __nonnull
; - 我们经常使用的
NSError **
通常是被假定为一个指向nullable
NSError 对象的nullable
指针。