扩展中的重写方法签名似乎在某些情况下产生不可预知的结果.以下示例演示了两种具有相似模式的不同结果.
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
你可以看到,当它被转换为A时,它会跳过B的doThing的实现,但是当转换为UIViewController时,它包括viewDidLoad的两个实现.这是预期的行为吗?如果是这样,这是什么原因?
ENV:Xcode 7.3,游乐场
解决方法
令人惊讶的是,编译器允许扩展中的覆盖.这不编译:
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() } }
在你的例子中,似乎编译器给你一个pass,因为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() } }
我的猜测是,你被允许这样做的事实本身就可能是一个bug. docs说:
Extensions can add new functionality to a type,but they cannot override existing functionality.
超越是没有被列为扩展可以做的事情之一.所以似乎这不应该编译.但是,如我之前所说,也许这是有意识的兼容Objective-C.无论哪种方式,我们正在探索一个边缘的情况,你非常好地引出了它的边缘.
特别地,上述代码仍然不会导致动态调度成为可操作的.这就是为什么你必须声明doThing是动态的,如@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() } }
这样做也是如此:
class A : NSObject { func doThing() { print("dothing super class") } } class B: A { override func doThing() { print("dothing sub class") super.doThing() } }
我的结论是:非常好的例子将其提交给苹果作为可能的错误;不要这样做在课堂上超越,而不是延伸.