覆盖扩展中的方法签名似乎会产生不可预测的结果。以下示例演示了具有类似模式的两种不同结果。
class A: UIViewController {
func doThing() {
print("dothing super class")
}
override func viewDidLoad() {
print("viewdidload superclass")
super.viewDidLoad()
}
}
class B: A { }
extension B {
override func doThing() {
print("dothing sub class")
super.doThing()
}
override func viewDidLoad() {
print("viewdidload subclass")
super.viewDidLoad()
}
}
let a: A = B()
a.doThing()
let vc: UIViewController = B()
vc.viewDidLoad()
打印:
dothing super class
viewdidload subclass
viewdidload superclass
当B
转换为doThing
时,您可以看到A
的{{1}}实现,但是在转换为{{1}时包含viewDidLoad
的两种实现}}。这是预期的行为吗?如果是这样,原因是什么?
ENV:Xcode 7.3,Playground
答案 0 :(得分:13)
这里的惊喜是编译器允许扩展中的覆盖。这个不编译:
class A {
func doThing() {
print("dothing super class")
}
}
class B: A {
}
extension B {
override func doThing() { // error: declarations in extensions cannot override yet
print("dothing sub class")
super.doThing()
}
}
在您的示例中,似乎编译器为您提供了一个传递,因为A派生自NSObject - 可能是为了允许此类与Objective-C进行交互。这个 编译:
class A : NSObject {
func doThing() {
print("dothing super class")
}
}
class B: A {
}
extension B {
override func doThing() {
print("dothing sub class")
super.doThing()
}
}
我的猜测是,你被允许做这个覆盖的事实本身可能是一个错误。 docs说:
扩展程序可以为类型添加新功能,但它们无法覆盖现有功能。
并且覆盖无法列为扩展可以做的事情之一。所以看起来这应该不编译。但是,正如我之前所说的那样,也许这是有意与Objective-C兼容的。无论哪种方式,我们正在探索一个边缘案例,你已经很好地引出了它的边缘。
特别是,前面的代码仍然不会导致动态调度变得可操作。这就是为什么你必须像@jtbandes所建议的那样将doThing
声明为dynamic
,或者将它放在实际的类而不是扩展中 - 如果你想要多态运行的话。因此,这可以按照您的预期进行:
class A : NSObject {
dynamic func doThing() {
print("dothing super class")
}
}
class B: A {
}
extension B {
override func doThing() {
print("dothing sub class")
super.doThing()
}
}
这样做:
class A : NSObject {
func doThing() {
print("dothing super class")
}
}
class B: A {
override func doThing() {
print("dothing sub class")
super.doThing()
}
}
我的结论是:非常好的例子;将其作为可能的错误提交给Apple;并且不要那样做。在课堂上重写,而不是在扩展中。