Swift - 如何覆盖具体子类中的扩展方法

Swift - How can I override an extension method in a concrete subclass

我在 UIView 上有一个实现协议的扩展

protocol SomeProtocol {
  var property : Int
}
    extension UIView : SomeProtocol {
      var property : Int {
        get {
          return 0
        }
        set {
          // do nothing
        }
      }
    }

在一个具体的子类中我想覆盖这个扩展方法:

class Subclass : UIView, SomeProtocol {
  var _property : Int = 1
  var property : Int {
    get { return _property}
    set(val) {_property = val}
  }
}

我设置了断点,看到调用了扩展方法而不是具体的子类方法:

var subclassObject = Subclass()

someObject.doSomethingWithConcreteSubclassObject(subclassObject)

// other code;

fun doSomethingWithConcreteSuclassObject(object : UIView) {
  var value = object.property // always goes to extension class get/set
}

我想你忘了覆盖子类中的超类 属性:

class Subclass : UIView {
    var _property : Int = 1
    override var property : Int {
        get { return _property}
        set(val) {_property = val}
    }
}

看起来您可以为第二个超级 class 属性 覆盖 属性。例如,您可以通过对要覆盖 UIViewframe 属性 的 UILabel 进行扩展来访问 UIView 属性。此示例适用于 Xcode 6.3.2

extension UILabel {

     override public var frame: CGRect {

         didSet {
             println("\(frame)")
        }
     }
}

一样,Swift 还不允许您覆盖 class 扩展中声明的方法。然而,我不确定你是否会得到你想要的行为,甚至 if/when Swift 有一天允许你覆盖这些方法。

考虑 Swift 如何处理协议和协议扩展。给定打印一些元语法变量名称的协议:

protocol Metasyntactic {
    func foo() -> String
    func bar() -> String
}

提供默认实现的扩展:

extension Metasyntactic {
    func foo() -> String {
        return "foo"
    }

    func bar() -> String {
        return "bar"
    }
}

和一个符合协议的class:

class FooBar : Metasyntactic {
    func foo() -> String {
        return "FOO"
    }

    func bar() -> String {
        return "BAR"
    }
}

Swift 将使用 动态调度 来根据每个变量的 调用 foo()bar() 的适当实现运行时类型 而不是编译器推断的类型:

let a = FooBar()
a.foo()  // Prints "FOO"
a.bar()  // Prints "BAR"

let b: Metasyntactic = FooBar()
b.foo()  // Prints "FOO"
b.bar()  // Prints "BAR"

但是,如果我们进一步扩展协议以添加新方法:

extension Metasyntactic {
    func baz() -> String {
        return "baz"
    }
}

如果我们在符合协议的 class 中覆盖我们的新方法:

class FooBarBaz : Metasyntactic {
    func foo() -> String {
        return "FOO"
    }

    func bar() -> String {
        return "BAR"
    }

    func baz() -> String {
        return "BAZ"
    }
}

Swift 现在将使用 static dispatch 根据编译器推断的类型调用 baz() 的适当实现:

let a = FooBarBaz()
a.baz()  // Prints "BAZ"

let b: Metasyntactic = FooBarBaz()
b.baz()  // Prints "baz"

Alexandros Salazar 有 a fantastic blog post explaining this behavior in depth,但足以说明 Swift 仅对原始协议中声明的方法使用动态调度, 方法在协议扩展中声明。我想 class 扩展也是如此。

我知道这个问题已经有人问过了。但这对于寻找更简单方法的人来说会很方便。有一种方法可以覆盖扩展方法。我知道它有点老套,但它做得很漂亮。

如果您使用 @objc

声明您的协议
@objc protocol MethodOverridable {
   func overrideMe()
}

扩展中

extension MainClass: MethodOverridable {
     func overrideMe() {
        print("Something useful")
     } 
}

子类 - 你可以在你的子类中覆盖它。它像魔术一样工作。好吧,在添加 @objc 时并不是真的,它会暴露您的 protocol to Objective-C 及其 Runtime。这允许您的子类覆盖。

 class SubClass: MainClass {
     override func overrideMe() {
          print("Something more useful")
      }  
  }

你不能通过正常方式做到这一点。

在 Apple 的文档中,您不能覆盖子类扩展中的方法。

此外,扩展可以为类型添加新功能,但不能覆盖现有功能。

https://docs.swift.org/swift-book/LanguageGuide/Extensions.html

Swift 5

class Class
{
    @objc dynamic func make() { print("make from class") }
}

class SubClass: Class {}

extension SubClass {
    override func make() {
        print("override")
    }
}