iosiOS开发你需要知道的

iOS nullable和nonnull的修饰语位置

2021-07-19  本文已影响0人  齐云霄霄

苹果在 Xcode 6.3 引入了一个 Objective-C 的新特性:Nullability Annotations,这一新特性的核心是两个新的类型修饰: __nullable 和 __nonnull 。从字面上我们可知, __nullable 表示对象可以是 NULL 或 nil,而__nonnull 表示对象不应该为空。当我们不遵循这一规则时,编译器就会给出警告。
在 Xcode 7 中,为了避免与第三方库潜在的冲突,苹果把 __nonnull/__nullable改成 _Nonnull/_Nullable 。再加上苹果同样支持了没有下划线的写法 nonnull/nullable ,于是就造成现在有三种写法这样混乱的局面。但是这三种写法本质上都是互通的,只是放的位置不同,举例如下:

  1. 方法返回值修饰
- (nullable NSString *)method;

- (NSString * __nullable)method;

- (NSString * _Nullable)method;
  1. 声明属性的修饰
@property (nonatomic, copy, nullable) NSString *aString;

@property (nonatomic, copy) NSString * __nullablea String;
 
@property (nonatomic, copy) NSString * _Nullable aString;
  1. 方法参数修饰:
- (void)methodWithString:(nullable NSString*)aString;
 
- (void)methodWithString:(NSString *_Nullable)aString;
 
- (void)methodWithString:(NSString *__nullable)aString;
  1. 而对于双指针类型对象 、 Block 的返回值 、 Block 的参数等,这时候就不能用 nonnull/nullable 修饰,只能用带下划线的 __nonnull/__nullable 或者 _Nonnull/_Nullable :
- (void)methodWithError:(NSError* _Nullable * _Nullable)error
 
- (void)methodWithError:(NSError* __nullable* __null_unspecified)error;
  1. 以及其他的组合方式
- (void)methodWithBlock:(nullable void(^)())block;

注意上面的 nullable 用于修饰方法传入的参数 Block 可以为空,而不是修饰 Block 返回值;

- (void)methodWithBlock:(void(^ _Nullable)())block;
 
- (void)methodWithBlock:(void(^ __nullable)())block;
 
- (void)methodWithBlock:(nullable id __nonnull(^)(id __nullable params))block;

注意上面的 nullable 用于修饰方法传入的参数 Block 可以为空,而 __nonnull 用于修饰 Block 返回值 id 不能为空

- (void)methodWithBlock:(id __nonnull(^ __nullable)(id __nullable params))block;
 
- (void)methodWithBlock:(id _Nonnull (^ _Nullable)(id _Nullable params))block;

以上基本上罗列了绝大部分的使用场景,但看完我们还是一脸懵逼啊,仍然不清楚什么时候应该用哪个修饰符!

在看了原生 iOS SDK 里 Foundation 和 UIKit 的头文件以及苹果的博文 《Nullability and Objective-C》 ,我们总结如下使用规范:

NS_ASSUME_NONNULL_BEGIN
 
@interface MyClass()

@property(nonatomic, copy) NSString *aString;

- (id)methodWithString:(nullable NSString*)str;

@end

NS_ASSUME_NONNULL_END

在上面的代码中,aString属性默认是nonnull的,methodWithString:方法的返回值也是nonnull,而方法的参数str被显式指定为nullable。

不过,为了安全起见,苹果还制定了以下几条规则:

  • 通过typedef定义的类型的nullability特性通常依赖于上下文,即使是在 Audited Regions 中,也不能假定它为nonnull;

  • 对于复杂的指针类型(如id *)必须显式去指定是nonnull还是nullable。例如,指定一个指向nullable对象的nonnull指针,可以使用__nullable id * __nonnull;

  • 我们经常使用的NSError,通常是被假定为一个指向nullableNSError 对象的nullable指针。

上一篇下一篇

猜你喜欢

热点阅读