在 Objective-C 中,我们总是在传递指针,并且指针总是可以是nil
. nil
许多 Objective-C 程序员利用发送消息什么都不做并返回0
//的事实nil
。NO
Swift 的处理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
和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 博客中的这篇文章所指出的那样)具有相同的效果。
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
并声明自己符合 this 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
一个没有定义非可选返回类型的方法。