web-dev-qa-db-fra.com

Swift - Comment puis-je remplacer une méthode d'extension dans une sous-classe concrète

J'ai une extension sur UIView implémentant un protocole

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

dans une sous-classe concrète, je veux remplacer cette méthode d'extension:

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

Je définis des points d'arrêt et vois que la méthode d'extension est appelée et non la méthode concrète de sous-classe:

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

Comme d'autres l'ont noté , Swift ne vous permet pas (encore) de remplacer une méthode déclarée dans une extension de classe. Cependant, je ne sais pas si vous obtiendrez le comportement souhaité, même si/quand Swift vous permettra un jour de remplacer ces méthodes.

Considérez comment Swift traite les protocoles et les extensions de protocole. Étant donné un protocole pour imprimer certains noms de variables métasyntaxiques:

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

Une extension pour fournir des implémentations par défaut:

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

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

Et une classe conforme au protocole:

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

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

Swift utilisera dynamic dispatch pour appeler les implémentations appropriées de foo() et bar() en fonction du type runtime de chaque variable plutôt que du type induit par le compilateur:

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

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

Cependant, si nous étendons le protocole pour ajouter une nouvelle méthode:

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

Et si nous remplaçons notre nouvelle méthode dans une classe conforme au protocole:

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

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

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

Swift utilisera maintenant static dispatch pour appeler l'implémentation appropriée de baz() en fonction du type induit par le compilateur:

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

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

Alexandros Salazar a un fantastique billet de blog expliquant ce comportement en profondeur , mais il suffit de dire que Swift n’utilise la répartition dynamique que pour les méthodes déclarées dans le protocole original, not pour les méthodes déclarées dans les extensions de protocole. J'imagine qu'il en irait de même pour les extensions de classe.

14
Chris Frederick

Je sais que cette question a été posée il y a un moment. Mais ce sera pratique pour quelqu'un qui cherche un moyen plus facile. Il existe un moyen de remplacer une méthode d'extension. Je sais son bit hacky mais il fait le travail magnifiquement.

Si vous déclarez votre protocole avec @objc 

@objc protocol MethodOverridable {
   func overrideMe()
}

En extension

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

Sous-classe - Vous pouvez le remplacer dans votre sous-classe. Cela fonctionne comme une magie. Eh bien, pas vraiment lorsque vous ajoutez @objc, cela expose votre protocol to Objective-C et sa Runtime. Cela permet à votre sous-classe de remplacer.

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

Il semble que vous puissiez remplacer la propriété pour la propriété 2nd super class. Par exemple, vous pouvez accéder à la propriété UIView en faisant en sorte que UILabel souhaite remplacer la propriété frame de UIView. Cet exemple fonctionne pour moi dans Xcode 6.3.2

extension UILabel {

     override public var frame: CGRect {

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