在 Objective-C 中,我们总是传递指针,并且指针总是可以nil
。许多 Objective-C 程序员利用了这样一个事实:发送消息到nil
什么也没做并返回0
/nil
/NO
。快速手柄nil
完全不同。对象要么存在(从不nil
),或者不知道它们是否存在(这就是 Swift 可选选项发挥作用的地方)。
在 Xcode 6.3 之前,这意味着任何使用 Objective-C 代码的 Swift 代码都必须将所有对象引用视为 Swift 选项。 Objective-C 的语言规则并没有阻止对象指针被nil
.
对于使用 Swift 中的 Objective-C 协议、类等来说,这意味着这是一个巨大的混乱。我们必须在不完美的解决方案之间做出选择。
给定以下 Objective-C 协议:
@protocol ObjCProtocol <NSObject>
@required + (id<ObjCProtocol>)classMethod;
@required - (id<ObjCProtocol>)instanceMethod;
@required - (void)methodWithArgs:(NSObject *)args;
@end
我们可以接受包含隐式解包选项的方法定义:
class MyClass: NSObject, ObjCProtocol {
func methodWithArgs(args: NSObject!) {
// do stuff with args
}
}
这使得生成的代码更干净(我们永远不必在正文中解包),但是我们始终面临“解包可选时发现 nil”错误的风险。
或者,我们可以将该方法定义为真正的可选方法:
class MyClass: NSObject, ObjCProtocol {
func methodWithArgs(args: NSObject?) {
// unwrap do stuff with args
}
}
但这给我们留下了很多混乱的解包代码。
Xcode 6.3 修复了这个问题,并为 Objective-C 代码添加了“Nullability Annotations”。
新引入的两个关键字是nullable
and nonnull
。它们用在为 Objective-C 代码声明返回类型或参数类型的同一位置。
- (void)methodThatTakesNullableOrOptionalTypeParameter:(nullable NSObject *)parameter;
- (void)methodThatTakesNonnullNonOptionalTypeParameter:(nonnull NSObject *)parameter;
- (nullable NSObject *)methodReturningNullableOptionalValue;
- (nonnull NSObject *)methodReturningNonNullNonOptionalValue;
除了这两个注释关键字之外,Xcode 6.3 还引入了一组宏,可以轻松地将大段 Objective-C 代码标记为nonnull
(根本没有注释的文件被有效地假设为nullable
)。为此,我们使用NS_ASSUME_NONNULL_BEGIN
在该部分的顶部和NS_ASSUME_NONNULL_END
在我们想要标记的部分的底部。
因此,例如,我们可以将整个协议包装在这个宏对中。
NS_ASSUME_NONNULL_BEGIN
@protocol CalcPrimesProtocol <NSObject>
- (void) calcPrimesWithComputeRecord: (ComputeRecord *) aComputeRecord
withUpdateDisplayBlock: (updateDisplayBlock) theUpdateDisplayBlock
andCompletionBlock: (calcPrimesCompletionBlock) theCalcPrimesCompletionBlock;
+ (id <CalcPrimesProtocol> ) sharedInstance;
@end
NS_ASSUME_NONNULL_END
这与将所有指针参数和返回类型标记为相同的效果nonnull
(但也有一些例外,正如 Apple 的 Swift 博客中的这篇文章所指出的 https://developer.apple.com/swift/blog/?id=25).
Xcode 6.3 之前的版本
符合 Objective-C 协议的 Swift 类必须将该协议中的任何 Objective-C 类型视为可选类型。
为了解决这个问题,我创建了以下 Objective-C 协议:
@protocol ObjCProtocol <NSObject>
@required + (id<ObjCProtocol>)classMethod;
@required - (id<ObjCProtocol>)instanceMethod;
@required - (void)methodWithArgs:(NSObject *)args;
@end
然后,创建一个继承自的 Swift 类NSObject
并声明自己符合此规定ObjCProtocol
.
然后我继续输入这些方法名称,并让 Swift 自动完成这些方法,这就是我得到的(我放入方法主体,其余的如果自动完成):
class ASwiftClass : NSObject, ObjCProtocol {
class func classMethod() -> ObjCProtocol! {
return nil
}
func instanceMethod() -> ObjCProtocol! {
return nil
}
func methodWithArgs(args: NSObject!) {
// do stuff
}
}
现在,我们可以使用常规选项(使用?
)而不是这些自动展开的选项(如果我们愿意的话)。编译器对其中任何一个都非常满意。关键是我们必须考虑到以下可能性:nil
,因为 Objective-C 协议无法阻止nil
从被通过。
如果这个协议在 Swift 中实现,我们可以选择返回类型是否可选,并且 Swift 会阻止我们返回nil
未定义非可选返回类型的方法。