Swift 4 中还不能覆盖来自扩展的声明

Declarations from extensions cannot be overridden yet in Swift 4

我最近将我的代码迁移到了 Swift 4。我面临 extensions 的问题,即:

Declarations from extensions cannot be overridden yet

我已经阅读了多篇对这个问题进行重新评分的帖子。但是他们中的 none 接受了下面描述的场景:

class BaseCell: UITableViewCell
{
    //Some code here...
}

extension BaseCell
{
    func isValid() -> String?
    {
        //Some code here...
    }
}

class SampleCell: BaseCell
{
    //Some code here...

    override func isValid() -> String? //ERROR..!!!
    {
        //Some code here...
    }
}

根据 Apple 的说法,

Extensions can add new functionality to a type, but they cannot override existing functionality.

但是在上面的场景中,我并没有覆盖扩展中的方法isValid()。它在 SampleCell class 定义本身中被覆盖。仍然,它给出了错误。

But in the above scenario, I am not overriding the method isValid() in an extension.

isValid 扩展 中声明。

错误基本上是说,如果以这种方式声明一个函数,它不能被覆盖

该语句对来自扩展在扩展中均有效。

我认为这是不言自明的。 声明 FROM 扩展还不能被覆盖

您正在尝试 overrideBaseCellextension 中声明的函数 func isValid() -> String?,而不是 BaseCell class本身。

它清楚地表明您不能覆盖在扩展中声明的内容。

希望对您有所帮助。

在 Swift 3 中,如果扩展是从 Objective-C (http://blog.flaviocaetano.com/post/this-is-how-to-override-extension-methods/) 派生的 class,您可以覆盖扩展的功能,但我想现在在 Swift 4 中是不可能的。你当然可以这样做:

protocol Validity {
    func isValid() -> String?
}

class BaseCell: UITableViewCell, Validity {

}

extension Validity
{
    func isValid() -> String? {
        return "false"
    }
}

class SampleCell: BaseCell {

    func isValid() -> String? {
        return "true"
    }
}


let base = BaseCell()
base.isValid() // prints false

let sample = SampleCell()
sample.isValid() // prints true

在Swift中无效,但在Objective-C中无效。因此,如果您的方法签名允许它(没有 objc 禁止构造),您可以声明它 @objc func myMethod() 并在 Swift.

中自由覆盖它

我也有大量的 Swift 3 代码,它们使用这个老技巧来实现我想要的,所以当我转到 Swift 4 并开始出现这些错误时,我有点苦恼。别怕,有办法的。

此错误与 Swift 4 编译 classes 的方式以及它处理 Objective-C classes 和函数的新方式有关。在 Swift 3 下,如果 class 派生自 NSObject,那么 class 中的所有变量和函数都将使用 Objective-C 的动态命名和查找约定。这种方法抑制了 Swift 优化代码和提高代码性能和大小的能力。

为了克服这些惩罚,在 Swift 4 中,只有明确标记为 @objc 的变量和函数得到 Objective-C 处理,其他所有内容都使用标准 Swift 约定:因此错误。

有了这些知识,您的问题的解决方案是将您希望覆盖的扩展中的函数标记为 @objc,然后在子 classes 中覆盖该函数,但请记住包含 @objc 标记,这样您的代码将在运行时被调用。

警告 这里有一个小问题:如果您忘记在 override 中包含 @objc,编译器不会报错,但是您的代码缺少动态查找,因此在运行时永远不会被调用。

所以您的代码应该看起来像这样:

class BaseCell: UITableViewCell {
    //Some code here...
}

extension BaseCell {
    @objc func isValid() -> String? {
        //Some code here...
    }
}

class SampleCell: BaseCell {
    //Some code here...

    @objc override func isValid() -> String? {
        //Some code here...
    }
}

只要您 @objc 协议,您就可以覆盖扩展中的声明。在 Swift 4.2:

class BaseClass {}
class SubclassOfBaseClass: BaseClass {}

@objc protocol IsValidable {
    func isValid() -> Bool
}

extension BaseClass: IsValidable {
    func isValid() -> Bool { return false }
}

extension SubclassOfBaseClass {
    override func isValid() -> Bool { return !super.isValid() }
}

BaseClass().isValid()           // -> false
SubclassOfBaseClass().isValid() // -> true