NS_ASSUME_NONNULL_BEGIN & NS_ASSUME_NONNULL_END

Aug 29, 2020 • 预计阅读时间 2 分钟

ObjC 里一切都是对象指针,对象之前传值是传指针引用。

Swift 里一切都是对象,参数传递分为值引用和对象引用。

在 ObjC 的方法里,可以判断传进来的指针是否为 nil

- (void)foo:(id)obj {
    if (!obj) {
        return;
    }
}

在 Swift 的方法里,只有 Optional (? & !) 才能判断是否为 nil

func foo(_ obj: Any?) {
    guard let obj = obj else {
        return
    }
}
func foo(_ obj: Any!) {
    guard let obj = obj else {
        return
    }
}

但是如果是非可选的类型,就不能在方法内部判断传进来的是不是 nil

func foo(_ obj: Any) {
    // obj must not be nil
}

如果是 nil 就会造成解包时发生异常而崩溃。

所以在 Swift 里通常是在方法外部保证参数的有效性,而 ObjC 则可以在方法内部做校验,不需要由外部保证有效性。

因为 Swift 需要明确指定参数是可选还是非可选类型,而 ObjC 里一切参数都可以是可选的。

所以在 Swift 调用 ObjC 方法的时候,为了代码风格能和 Swift 兼容,Apple 官方从 Xcode 9 开始提供了 NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END 宏, 在这两个宏包围的范围内,所有的指针类型对应到 Swift 里都是非可选类型,开发者只需要标明哪些参数是可选类型就可以了。

表明是可选参数的标识符是 nullable_Nullable ,对应不同的使用场景:

nullable 可以用在属性、返回值和方法的参数里,而且位置是在类型的前面:

@property(nullable, nonatomic) NSString *reusableIdentifier;

- (nullable NSString *)reusableIdentifier;
- (void)setReusableIdentifier:(nullable NSString *)reusableIdentifier;

_Nullable 是要放在参数的前面,类型的后面:

@property(nonatomic) NSString * _Nullable reusableIdentifier;

- (NSString * _Nullable)reusableIdentifier;
- (void)setReusableIdentifier:(NSString * _Nullable)reusableIdentifier;

还有以下场景只能使用 _Nullable

@property(nonatomic, copy) void (^completion)(id _Nullable Obj, NSError * _Nullable error);

- (BOOL)isDirectory:(NSString *)path error:(NSError * __autoreleasing _Nullable * _Nullable)error;

以上例子中的 error 类型是 NSError **,有几个星号就需要写几个 _Nullable (按实际情况,必须有值就是 _Nonnull)。

__autoreleasing 修饰是把对象加入到 AutoreleasePool 中保证能自动释放,当然不写编译器也会把 NSError ** 处理成 NSError * __autoreleasing *

iOS
版权声明:如果转发请带上本文链接和注明来源。

lvv.me

iOS/macOS Developer

*** -[UIKeyboardLayoutStar release]: message sent to deallocated instance

pmset 合盖省电设置