Swift调度到子类扩展中的重写方法

时间:2016-05-27 22:03:58

标签: ios swift

在某些情况下,

覆盖扩展中的方法签名似乎会产生不可预测的结果。以下示例演示了具有类似模式的两种不同结果。

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

1 个答案:

答案 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;并且不要那样做。在课堂上重写,而不是在扩展中。