2015-04-27 29 views
14

Mam rozszerzenie na UIView wdrażania ProtokołuSwift - W jaki sposób można zastąpić metodę rozszerzenia w konkretnej podklasy

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

w konkretnej podklasy Chcę, aby zastąpić tę metodę rozszerzenia:

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

Ustawiam punkty przerwania i widzę, że wywoływana jest metoda rozszerzenia, a nie metoda konkretnej podklasy:

var subclassObject = Subclass() 

someObject.doSomethingWithConcreteSubclassObject(subclassObject) 

// other code; 

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

Próbowałem również dodać nadpisanie, ale nadklasa nie ma tej właściwości –

+2

W jakiej wersji Swift używasz? Tutaj z Swift 1.2 i Xcode 6.3.1 pojawia się następujący błąd podczas kompilacji, gdy próbujesz przesłonić właściwość _extension_: "Deklaracje z rozszerzeń nie mogą być jeszcze zastąpione" ... i _yet_ sugeruje mi, że będą one w przyszłości. –

+0

Interesujące, więc wygląda na to, że ta funkcja pojawi się w przyszłej wersji. –

Odpowiedz

12

Jak others have noted Swift ma (jeszcze) nie pozwalają przesłonić metodę zadeklarowaną w rozszerzeniu klasy. Jednak nie jestem pewien, czy kiedykolwiek uzyskasz pożądane zachowanie, nawet jeśli/kiedy Swift pewnego dnia pozwoli ci na zastąpienie tych metod.

Zastanów się, jak Swift radzi sobie z protokołami i rozszerzeniami protokołów. Biorąc pod uwagę protokół wydrukować kilka metasyntactic nazw zmiennych:

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

rozszerzeniem zapewniają domyślne implementacje:

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

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

oraz klasę, która jest zgodna z protokołem:

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

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

Swift użyje dynamiczna wysyłka wywoływanie odpowiednich implementacji foo() i bar() ba sed na każdej zmiennej typu wykonawczego zamiast typu wywnioskowane przez kompilator:

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

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

Jeśli jednak możemy rozszerzyć protokół ponadto, aby dodać nową metodę:

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

i jeśli zastąpić naszą nową metodę w klasie, który jest zgodny z protokołem:

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

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

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

Swift będzie teraz używać statycznego wysyłki aby wywołać odpowiednią realizację baz() na podstawie typu wywnioskowane przez kompilator:

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, ale wystarczy powiedzieć, że tylko Swift wykorzystuje dynamiczne wysyłkę dla metod zadeklarowanych w oryginalnym protokole, nie dla metod zadeklarowanych w rozszerzeniach protokołu. Wyobrażam sobie, że to samo dotyczy rozszerzeń klas.

+0

Znakomita odpowiedź, dziękuję. – OhadM

-3

Chyba zapomnieli zastąpić właściwość nadrzędnej w swojej podklasy:

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

Próbowałem również. Otrzymuje błąd składniowy: "Właściwość nie zastępuje żadnej właściwości z jej nadklasy" –

+0

To dziwne. Wygląda na to, że twoja podklasa nie może uzyskać dostępu do właściwości super. Czy są one zadeklarowane w tym samym pliku źródłowym? Być może trzeba jawnie zadeklarować publicznie. –

+1

Zobacz http://stackoverflow.com/questions/27109006/can-you-override-between-extensions-in-swift-or-not-compiler-seems-confused – Moritz

0

Wygląda można zastąpić nieruchomości na 2 Super właściwości klasy. Na przykład można uzyskać dostęp do właściwości UIView, tworząc rozszerzenie do UILabel, które ma zastąpić właściwość frame z UIView. Próbka ta działa na mnie w Xcode 6.3.2

extension UILabel { 

    override public var frame: CGRect { 

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

Wiem, że to pytanie zostało zadane jakiś czas temu. Będzie to jednak przydatne dla kogoś, kto szuka łatwiejszego sposobu. Istnieje sposób na przesłonięcie metod rozszerzenia. Wiem, że jest trochę hacky, ale robi to pięknie.

Jeśli zadeklarować swój protokół z @objc

@objc protocol MethodOverridable { 
    func overrideMe() 
} 

w rozszerzeniu

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

Podklasa - można stanie zastąpić go w swojej podklasy. Działa jak magia. Cóż, nie tak naprawdę dodając @objc, odsłania swój protocol to Objective-C i jego Runtime. To pozwala nadpisać twoją podklasę.

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