我对Swift和Objective-C都不陌生,但是Error今天在使用一个子类型时,我看到了一些奇怪的行为,这使我更加深入了。
Error
使用NSString子类时(是的,下面的示例对 不 基于的类具有类似的功能NSObject):
NSString
NSObject
import Foundation // Class version class OddString : NSString { override var description: String { return "No way, José" } } let odd = OddString() func printIt(_ string: NSString) { print(string.description) } print(odd.description) printIt(odd)
我看到了我期望看到的:
No way, José No way, José
但是,当我使用struct()编写( 我认为是 )等效代码时Error:
import Foundation // Struct version struct TestError : Error { var localizedDescription: String { return "I am a TestError" } } let explosive = TestError() func printIt(_ error : Error) { print(error.localizedDescription) } print(explosive.localizedDescription) printIt(explosive)
我懂了:
I am a TestError The operation couldn’t be completed. (SanityChecks.TestError error 1.)
这真让我感到困惑。是否确定在 编译时 将对传入的结构调用什么方法printIt,而不管它 实际上 是什么类型?
printIt
进一步:《 Swift编程指南》中记录了类和结构之间在运行时行为上的区别,有人可以引用此部分吗?我还没有找到任何东西。
在你的第一个例子中,你要 重写 的description属性。因此,此实现被添加到OddString的vtable中(因为它是一个 类 ),并且可以动态地分派到正确的状态,而不管实例的静态类型是什么。
description
OddString
在第二个示例中,您没有类-因此没有vtables。但是,您 正在 遵守协议。协议允许通过协议见证表进行动态调度(请参阅WWDC的有关此表的详细介绍),但是这仅在协议 要求的 实现中发生。
localizedDescription不是协议的协议要求Error,它仅在Error您何时使用的协议扩展中定义import Foundation(在SE-0112中进行了说明)。因此,它不能动态调度。相反,它将是静态分派的-因此调用的实现取决于实例的 静态类型 。
localizedDescription
import Foundation
这就是您在此处看到的行为–将explosive实例键入为时TestError,将localizedDescription调用您的实现。当键入为时Error,将Error调用扩展中的实现(它只是NSError与之连接并获取localizedDescription)。
explosive
TestError
NSError
如果要提供本地化的描述,则应LocalizedError改用错误类型,将其定义errorDescription为协议要求,从而可以动态分派。
LocalizedError
errorDescription