it-swarm.com.de

Swift - Wie kann ich eine Erweiterungsmethode in einer konkreten Unterklasse überschreiben?

Ich habe eine Erweiterung für UIView, die ein Protokoll implementiert

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

in einer konkreten Unterklasse möchte ich diese Erweiterungsmethode überschreiben:

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

Ich setze Haltepunkte und sehe, dass die Erweiterungsmethode aufgerufen wird und nicht die konkrete Unterklassenmethode:

var subclassObject = Subclass()

someObject.doSomethingWithConcreteSubclassObject(subclassObject)

// other code;

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

Wie andere bemerkt haben , erlaubt Swift (noch) nicht, eine in einer Klassenerweiterung deklarierte Methode zu überschreiben. Ich bin mir jedoch nicht sicher, ob Sie jemals das gewünschte Verhalten erhalten, auch wenn Swift es Ihnen irgendwann erlaubt, diese Methoden zu überschreiben.

Beachten Sie, wie Swift mit Protokollen und Protokollerweiterungen umgeht. Ein Protokoll zum Drucken einiger metasyntaktischer Variablennamen gegeben:

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

Eine Erweiterung zur Bereitstellung von Standardimplementierungen:

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

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

Und eine Klasse, die dem Protokoll entspricht:

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

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

Swift verwendet dynamischer Versand, um die entsprechenden Implementierungen von foo() und bar() basierend auf Laufzeittypjeder Variablen aufzurufen, und nicht auf dem vom Compiler abgeleiteten Typ:

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

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

Wenn wir jedoch das Protokoll erweitern, wird eine neue Methode hinzugefügt:

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

Und wenn wir unsere neue Methode in einer Klasse überschreiben, die dem Protokoll entspricht:

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

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

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

Swift verwendet jetzt statische Abgabe, um die entsprechende Implementierung von baz() basierend auf dem vom Compiler abgeleiteten Typ aufzurufen:

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

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

Alexandros Salazar hat ein fantastischer Blog-Post, der dieses Verhalten ausführlich erklärt , aber es genügt zu sagen, dass Swift den dynamischen Versand nur für im ursprünglichen Protokoll deklarierte Methoden verwendet, nicht für in Protokollerweiterungen deklarierte Methoden. Ich kann mir vorstellen, dass das auch für Klassenerweiterungen gilt.

14
Chris Frederick

Ich weiß, dass diese Frage schon vor einiger Zeit gestellt wurde. Dies ist jedoch praktisch für jemanden, der einen einfacheren Weg sucht. Es gibt eine Möglichkeit, Erweiterungsmethoden zu überschreiben. Ich weiß, dass es ein bisschen hackig ist, aber es macht die Arbeit wunderbar.

Wenn Sie Ihr Protokoll mit @objc angeben 

@objc protocol MethodOverridable {
   func overrideMe()
}

In Erweiterung

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

Unterklasse - Sie können sie in Ihrer Unterklasse überschreiben. Es funktioniert wie ein Zauber. Nun, nicht wirklich, wenn Sie @objc hinzufügen, wird Ihr protocol to Objective-C und seine Runtime angezeigt. Dadurch kann Ihre Unterklasse überschreiben.

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

Es scheint, dass Sie die Eigenschaft für die 2. Superklasse überschreiben können. Sie können beispielsweise auf die Eigenschaft UIView zugreifen, indem Sie die Erweiterung UILabel verwenden, um die Eigenschaft frame von UIView zu überschreiben. Dieses Beispiel funktioniert für mich in Xcode 6.3.2

extension UILabel {

     override public var frame: CGRect {

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