Swift - 如何在具体子类中重写扩展方法

19

我有一个实现协议的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
}

我也尝试过添加override,但是超类没有这个属性。 - Avba
2
你使用的是哪个Swift版本?我这里使用的是Swift 1.2和Xcode 6.3.1,当你尝试覆盖_extension_属性时,会出现以下编译时错误:“扩展中的声明目前还无法被覆盖”,而_yet_则暗示着它们将来会被支持。 - Matteo Piombo
有趣,看起来这个功能将在未来的版本中推出。 - Luca Angeletti
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Avba
1
尝试使用Swift 2.0,如果您有“override”,则似乎可以编译正常,但仍然引用“扩展”值而不是子类。 - sbarow
6个回答

18

正如其他人所指出的,Swift目前(尚)不允许您覆盖在类扩展中声明的方法。然而,即使Swift有一天允许您覆盖这些方法,我也不确定您是否能获得所需的行为。

考虑Swift处理协议和协议扩展的方式。给定一个打印某些元语变量名称的协议:

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

提供默认实现的扩展:

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

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

实现该协议的类:

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 FooBarBaz : Metasyntactic {
    func foo() -> String {
        return "FOO"
    }

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

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

Swift现在将使用静态分发,根据编译器推断的类型来调用baz()的适当实现:

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

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

Alexandros Salazar写了一篇 极好的博客文章,详细解释了这种行为,但简单来说,Swift仅对原始协议中声明的方法使用动态分派,而不对协议扩展中声明的方法使用动态分派。我想类扩展也是如此。


3

我知道这个问题已经被问了一段时间。但对于那些寻找更简单方法的人来说,这将非常有用。有一种覆盖扩展方法的方法。我知道这有点hacky,但它可以完美地完成工作。

如果你使用@objc声明你的协议

@objc protocol MethodOverridable {
   func overrideMe()
}

In Extension

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

子类 - 你可以在子类中重写它。这就像魔法一样的东西。但是,当你添加 @objc 后,它会将你的 协议暴露给Objective-C 和它的 运行时。这使得你的子类可以进行重写。

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

1

Swift 5

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

class SubClass: Class {}

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

我认为这并没有增加任何已经在被接受的答案中存在的价值。 - ryanwebjackson
在被接受的答案中没有提到动态。 - AlexNikov

0

0

看起来你可以覆盖第二个超类的属性。例如,你可以通过对想要覆盖UIViewframe属性进行扩展,来访问UILabel的属性。这个示例在我的Xcode 6.3.2中有效。

extension UILabel {

     override public var frame: CGRect {

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

-3

我认为你忘记在子类中覆盖超类属性了:

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

我也尝试过了。它会出现语法错误:"属性没有覆盖其超类的任何属性"。 - Avba
很奇怪。似乎你的子类无法访问超类的属性。它们在同一个源文件中声明了吗?你可能需要明确声明为public。 - József Vesza
1
请查看https://dev59.com/m14d5IYBdhLWcg3wJvwp。 - Eric Aya
1
该属性在父类中未定义。该属性在协议中定义。 - Avba
由于属性属于协议:是否有引入继承的理由?您可以只让您的类符合协议而无需子类化。 - József Vesza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接