在Objective-C中,我们总是传递指针,指针可以为nil。许多Objective-C程序员利用了发送消息到nil时什么都不做并返回0/nil/NO的事实。Swift完全以不同的方式处理nil。对象要么存在(从不为nil),要么无法确定它们是否存在(这就是Swift可选项起作用的地方)。
因此,在Xcode 6.3之前,这意味着使用任何Objective-C代码的任何Swift代码都必须将所有对象引用视为Swift可选项。Objective-C的语言规则没有阻止对象指针为nil。
对于使用Objective-C协议、类等的Swift,这意味着一团糟。我们不得不在两个非完美的解决方案之间选择。
给定以下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!) {
}
}
这样做可以使代码更加简洁(我们不必在正文中解包),但我们总是有可能遇到“在解包可选项时发现了空值”错误。
或者,我们可以将该方法定义为真正的可选项:
class MyClass: NSObject, ObjCProtocol {
func methodWithArgs(args: NSObject?) {
}
}
但是这会留下许多混乱的解包代码。
Xcode 6.3解决了这个问题,并为Objective-C代码添加了“Nullability Annotations”。
引入了两个关键词:nullable和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
具有相同的效果(
除了若干例外,正如苹果的Swift博客中所指出的那样)。
在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
然后,创建了一个继承自NSObject
的Swift类,并声明自己符合这个ObjCProtocol
。
然后,我接着输入这些方法名,让Swift自动帮我补全方法,这就是我得到的结果(我加入了方法体,其余为自动补全):
class ASwiftClass : NSObject, ObjCProtocol {
class func classMethod() -> ObjCProtocol! {
return nil
}
func instanceMethod() -> ObjCProtocol! {
return nil
}
func methodWithArgs(args: NSObject!) {
}
}
现在,如果我们想要的话,可以使用常规可选项(带有
?
)代替这些自动解包的可选项。编译器对两者都非常满意。关键是我们必须允许可能出现
nil
的情况,因为Objective-C协议无法防止传递
nil
。如果这个协议在Swift中实现,我们可以选择返回类型是否为可选项,而Swift会阻止我们向没有定义非可选项返回类型的方法返回
nil
。
class func
应该返回符合CalcPrimesProtocol
协议的AnyObject
,而不是协议对象本身吗? - ravron