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

你可以看到,当它被转换为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()
    }
}

我的结论是:非常好的例子将其提交给苹果作为可能的错误;不要这样做在课堂上超越,而不是延伸.

相关文章

当我们远离最新的 iOS 16 更新版本时,我们听到了困扰 Apple...
欧版/美版 特别说一下,美版选错了 可能会永久丧失4G,不过只...
一般在接外包的时候, 通常第三方需要安装你的app进行测...
前言为了让更多的人永远记住12月13日,各大厂都在这一天将应...