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_BEGIN
和 NS_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 *
。