Objective-C 中的 Nullability

自从 Swift 发布,就有一个很重要的特性:Optional,提供了更严格的类型检查,使得在编译时就能发现更多空变量问题。同时我们知道的是 Swift 可以与 Objective-C 混合编译,那么 Swift 中的 Optional 特性如何与 Objective-C 兼容呢?同时 Nullability 给 Objective-C 带来哪些改进?

0x00 全新的修饰符

苹果在 Xcode 6.3 中引入了 Nullability 特性,这一新特性的核心就是两个新的修饰符:__nullable__nonnull,顾名思义前者为可空修饰,后者为不可空修饰,在编译时编译器会对此规则进行检查,并对未遵守规则的代码给出警告(在 Swift 中是无法通过编译的)。

但是为什么又有 _Nullable_Nonnull 呢?这两个新的写法是在 Xcode 7 中新增的,苹果给出的理由是:Due to potential conflicts with third-party libraries,为了避免与第三方库潜在的冲突。

但是又特么的为什么有 nullablenonnull 呢?根据苹果的说法,其实就是为了方便书写新增的。

tm

综上所述,__nullable_Nullablenullable 作用是一样的,同理 __nonnull_Nonnullnonnull 的作用也是一样的,但 __nullable__nonnull 算是不推荐使用了,输入也麻烦,所以接下来的例子就不讨论这两个了。

0x01 该怎么用

属性声明修饰:

1
2
@property (nonatomic, copy, nullable) NSString *name;
@property (nonatomic, copy) NSString * _Nullable name;

方法返回值修饰:

1
2
- (nullable NSString *)method;
- (NSString * _Nullable)method;

方法参数修饰:

1
2
3
4
- (void)methodWithString:(nullable NSString *)string;
- (void)methodWithString:(NSString * _Nullable)string;
- (void)methodWithBlock:(nullable void (^)())block;
- (void)methodWithBlock:(nonnull void (^)())block;

虽然说 nullablenonnull 看起来最顺眼,但不是所有场景都能使用的,因此举一堆例子🌰:

双指针类型对象修饰:

1
- (void)methodWithError:(NSError * _Nullable * _Nullable)error;

Block 返回值修饰:

1
2
- (void)methodWithBlock:(NSString * _Nullable(^)())block;
- (void)methodWithBlock:(NSString * _Nonnull(^)())block;

组合情况修饰:

1
- (void)methodWithBlock:(nullable NSString * _Nullable(^)(NSError * _Nullable * _Nullable error))block;

哈!是不是有点烧脑了~

0x02 Audited Regions

如果一个文件中大量 property 或者方法都要设置 nonnull,那么我们就要重复多次添加对应的修饰符,那就很麻烦了,因此 Xcode 给我们提供了 Audited Regions Annotation 来简化这些操作。

在需要标记的区域头尾分别添加 NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END 就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
NS_ASSUME_NONNULL_BEGIN

@interface UUObject ()

@property (nonatomic, copy) NSString *lastName; // 默认为 nonnull
@property (nonatomic, copy, nullable) NSString *firstName; // 指定为 nullable

- (void)methodAWithString:(NSString *)string; // 默认为 nonnull
- (void)methodBWithString:(nullable NSString *)string; // 指定为 nullable

@end

NS_ASSUME_NONNULL_END

0x03 总结

使用 nonnullnullable

  • 属性
  • 方法返回值
  • 方法参数

使用 _Nonnull_Nullable

  • C 函数相关参数
  • Block 参数
  • Block 返回值

__nonnull__nullable?不存在的。