Swift中的动态/运行时调度,或"奇怪的结构行为在一个人看来"

Cra*_*tis 4 error-handling struct dynamic nslocalizedstring swift

我不是Swift的新手,也不是Objective-C的新手,但是我Error今天看到一些奇怪的行为,这使得我更深入地挖掘了一个子类型.

使用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)
Run Code Online (Sandbox Code Playgroud)

我看到了我期望看到的东西:

No way, José
No way, José
Run Code Online (Sandbox Code Playgroud)

但是,当我使用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)
Run Code Online (Sandbox Code Playgroud)

我知道了:

I am a TestError
The operation couldn’t be completed. (SanityChecks.TestError error 1.)
Run Code Online (Sandbox Code Playgroud)

这对我来说真的很困惑.是否在编译时决定在传入的struct上调用哪个方法printIt,而不管它实际是什么类型?

此外:Swift编程指南中记录的类和结构之间的运行时行为有何不同,有人可以参考该部分吗?我还没有找到任何东西.

Ham*_*ish 6

在您的第一个示例中,您将覆盖该description属性.因此,将此实现添加到OddStringvtable(因为它是一个类),并且可以动态调度到恰好,无论实例静态类型是什么.

在你的第二个例子中,你没有一个类 - 所以没有vtable.然而,你是符合的协议.协议允许通过协议见证表进行动态调度(参见这篇关于它们的伟大的WWDC讲话),但这仅适用于协议要求的实现.

localizedDescription不在的协议要求Error协议,它只是在一个协议扩展定义的Error,当你import Foundation(这是在记录SE-0112).因此无法动态调度.相反,它将被静态分派 - 因此调用的实现依赖于实例的静态类型.

这就是你在这里看到的行为 - 当你的explosive实例被输入时TestError,你的实现就localizedDescription被调用了.当输入为时Error,Error调用扩展中的实现(它只是桥接NSError并获取它localizedDescription).

如果您想提供本地化描述,那么您应该将您的错误类型LocalizedError与之相符,它定义errorDescription为协议要求 - 从而允许动态调度.有关如何解决此问题的示例,请参阅此问答.