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

aht*_*ney 14 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()
Run Code Online (Sandbox Code Playgroud)

这打印:

dothing super class
viewdidload subclass
viewdidload superclass
Run Code Online (Sandbox Code Playgroud)

你可以看到这个跳过B的的实现doThing时,它铸造成A,但包括两个实现viewDidLoad时铸成UIViewController.这是预期的行为吗?如果是这样,原因是什么?

ENV:Xcode 7.3,游乐场

mat*_*att 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的示例中,似乎编译器为您提供了一个传递,因为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()
    }
}
Run Code Online (Sandbox Code Playgroud)

我的猜测是,你被允许做这个覆盖的事实本身可能是一个错误.该文件说:

扩展可以为类型添加新功能,但它们不能覆盖现有功能.

并且覆盖无法列为扩展可以做的事情之一.所以,看起来这应该不是编译.但是,正如我之前所说的那样,也许这是有意与Objective-C兼容的.无论哪种方式,我们正在探索一个边缘情况,你已经很好地引出了它的边缘.

特别是,前面的代码仍然不会导致动态调度变得可操作.这就是为什么你要doThingdynamic@jtbandes所说的那样声明为或者把它放在实际的类而不是扩展中 - 如果你想要多态运行的话.因此,这可以按照您的预期方式工作:

class A : NSObject {
    dynamic func doThing() {
        print("dothing super class")
    }
}
class B: A {
}
extension B {
    override func doThing() {
        print("dothing sub class")
        super.doThing()
    }
}
Run Code Online (Sandbox Code Playgroud)

这样做:

class A : NSObject {
    func doThing() {
        print("dothing super class")
    }
}
class B: A {
    override func doThing() {
        print("dothing sub class")
        super.doThing()
    }
}
Run Code Online (Sandbox Code Playgroud)

我的结论是:非常好的例子; 将其作为可能的错误提交给Apple; 并且不要那样做.在课堂上重写,而不是在扩展中.