2016-05-27 7 views
13

überschreiben Methode Signaturen in Erweiterungen scheint in bestimmten Fällen unvorhersehbare Ergebnisse zu erzeugen. Das folgende Beispiel zeigt zwei verschiedene Ergebnisse mit einem ähnlichen Muster.Swift-Dispatch zu überschreiben Methoden in Unterklassenerweiterungen

class A: UIViewController { 
    func doThing() { 
     print("dothing super class") 
    } 

    override func viewDidLoad() { 
     print("viewdidload superclass") 
     super.viewDidLoad() 
    } 
} 

class B: A { } 

extension B { 
    override func doThing() { 
     print("dothing sub class") 
     super.doThing() 
    } 

    override func viewDidLoad() { 
     print("viewdidload subclass") 
     super.viewDidLoad() 
    } 
} 

let a: A = B() 
a.doThing() 

let vc: UIViewController = B() 
vc.viewDidLoad() 

Diese Drucke:

dothing super class 
viewdidload subclass 
viewdidload superclass 

Sie können sehen, das die B ‚s Implementierung von doThing überspringt, wenn er als A gegossen wird, jedoch beide Implementierungen von viewDidLoad enthält, wenn sie als UIViewController gegossen. Ist das das erwartete Verhalten? Wenn ja, was ist der Grund dafür?

ENV: Xcode 7.3, Spielplatz

+7

Versuchen Sie 'dynamic func doThing()' in der Oberklasse. – jtbandes

+0

@jtbandes Das erzeugt das erwartete Ergebnis (dh die Aufrufe sowohl der Unterklassenimplementierung als auch der Superklassenimplementierung). Aber ich bin nicht klar, warum das erforderlich wäre und anders als das Verhalten bei der Umwandlung in 'UIViewController'. Unterscheiden sich die Verhaltensweisen auf Basis des Modulumfangs? – ahtierney

Antwort

12

Die Überraschung dabei ist, dass der Compiler die Überschreibung in der Verlängerung ermöglicht. Diese nicht Kompilierung:

class A { 
    func doThing() { 
     print("dothing super class") 
    } 
} 
class B: A { 
} 
extension B { 
    override func doThing() { // error: declarations in extensions cannot override yet 
     print("dothing sub class") 
     super.doThing() 
    } 
} 

In Ihrem Beispiel scheint es, dass der Compiler Sie einen Pass gibt, weil A von NSObject leitet sich - vermutlich, um diese Klasse zu ermöglichen, mit Objective-C zu interagieren. Dieses tut Kompilierung:

class A : NSObject { 
    func doThing() { 
     print("dothing super class") 
    } 
} 
class B: A { 
} 
extension B { 
    override func doThing() { 
     print("dothing sub class") 
     super.doThing() 
    } 
} 

Meine Vermutung ist, dass die Tatsache, dass Sie erlaubt sind überhaupt diese Überschreibung zu tun ist, sich möglicherweise um einen Fehler. Die docs sagen:

Erweiterungen können neue Funktionalität zu einem Typ hinzufügen, aber sie können vorhandene Funktionalität nicht überschreiben.

Und Überschreiben wird nirgends als eines der Dinge aufgeführt, die eine Erweiterung tun kann. So scheint es, dass dies nicht kompilieren sollte. Möglicherweise ist dies jedoch, wie bereits erwähnt, absichtlich für die Kompatibilität mit Objective-C erlaubt. So oder so, wir erforschen dann einen Randfall, und Sie haben sehr schön seine Nervosität ausgelöst.

Insbesondere führt der vorangehende Code immer noch nicht dazu, dass der dynamische Versand betriebsbereit wird. Deshalb müssen Sie entweder doThing als dynamic deklarieren, wie von @jtbandes vorgeschlagen, oder es in die tatsächliche Klasse anstatt die Erweiterung einfügen - wenn Sie wollen, dass Polymorphie funktioniert. So funktioniert das wie erwartet:

class A : NSObject { 
    dynamic func doThing() { 
     print("dothing super class") 
    } 
} 
class B: A { 
} 
extension B { 
    override func doThing() { 
     print("dothing sub class") 
     super.doThing() 
    } 
} 

Und so tut dies:

class A : NSObject { 
    func doThing() { 
     print("dothing super class") 
    } 
} 
class B: A { 
    override func doThing() { 
     print("dothing sub class") 
     super.doThing() 
    } 
} 

Meine Schlussfolgerung wäre: Sehr schönes Beispiel; reiche es als möglichen Fehler bei Apple ein; und tu das nicht. Überschreibe in der Klasse, nicht in der Erweiterung.

Verwandte Themen