2015-08-03 3 views
8

Ho avuto un problema con le estensioni del protocollo di Swift 2 con le implementazioni predefinite. L'essenziale è che ho fornito un'implementazione predefinita di un metodo di protocollo che sto scavalcando in una classe che implementa il protocollo. Quel metodo di estensione del protocollo viene chiamato da una classe base, che chiama quindi un metodo che ho sovrascritto in una classe derivata. Il risultato è che il metodo sottoposto a override non viene chiamato.Estensione del protocollo Swift 2 che non chiama correttamente il metodo sovrascritto

Ho provato a distillare il problema nel campo più piccolo possibile che illustra il problema di seguito.

protocol CommonTrait: class { 
    func commonBehavior() -> String 
} 

extension CommonTrait { 
    func commonBehavior() -> String { 
     return "from protocol extension" 
    } 
} 

class CommonThing { 
    func say() -> String { 
     return "override this" 
    } 
} 

class ParentClass: CommonThing, CommonTrait { 
    override func say() -> String { 
     return commonBehavior() 
    } 
} 

class AnotherParentClass: CommonThing, CommonTrait { 
    override func say() -> String { 
     return commonBehavior() 
    } 
} 

class ChildClass: ParentClass { 
    override func say() -> String { 
     return super.say() 
     // it works if it calls `commonBehavior` here and not call `super.say()`, but I don't want to do that as there are things in the base class I don't want to have to duplicate here. 
    } 
    func commonBehavior() -> String { 
     return "from child class" 
    } 
} 

let child = ChildClass() 
child.say() // want to see "from child class" but it's "from protocol extension” 
+0

vi consiglio di leggere questo post qui per capire come funziona: http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future – DevAndArtist

risposta

7

Sfortunatamente i protocolli non hanno un comportamento così dinamico (ancora).

Ma è possibile farlo (con l'aiuto delle classi) implementando commonBehavior() nello ParentClass e sovrascrivendolo nello ChildClass. È inoltre necessario CommonThing o un'altra classe di conformarsi alle CommonTrait, che è poi la superclasse di ParentClass:

class CommonThing: CommonTrait { 
    func say() -> String { 
     return "override this" 
    } 
} 

class ParentClass: CommonThing { 
    func commonBehavior() -> String { 
     // calling the protocol extension indirectly from the superclass 
     return (self as CommonThing).commonBehavior() 
    } 

    override func say() -> String { 
     // if called from ChildClass the overridden function gets called instead 
     return commonBehavior() 
    } 
} 

class AnotherParentClass: CommonThing { 
    override func say() -> String { 
     return commonBehavior() 
    } 
} 

class ChildClass: ParentClass { 
    override func say() -> String { 
     return super.say() 
    } 

    // explicitly override the function 
    override func commonBehavior() -> String { 
     return "from child class" 
    } 
} 
let parent = ParentClass() 
parentClass.say()   // "from protocol extension" 
let child = ChildClass() 
child.say()    // "from child class" 

Dal momento che questo è solo un breve soluzione per il problema mi auguro che si inserisce nel progetto.

+0

Un altro bel modo per risolvere questo problema in modo più specifico (non come generale. Solo 'ChildClass' avrebbe questo comportamento) è sostituendo il codice nella funzione' say() 'di' ParentClass' con 'return (self as? ChildClass) ? commonBehavior() ?? commonBehavior() '(utilizzando l'esempio di codice della domanda). – Qbyte

+2

Ciò significa che la classe genitrice deve sapere della sottoclasse, però, no? Piuttosto rompe l'incapsulamento. – Remover

+0

@Rimuovi hai ragione, la superclasse dovrebbe sapere qualcosa sulle sue sottoclassi che effettivamente rompono l'incapsulamento. Quindi questo dovrebbe essere solo un esempio di cos'altro potresti fare, anche se non lo consiglierei. – Qbyte

1

Per semplificare la mia mancanza di ciò che la parola "Yet" significa in un errore non specifico. Ho capito, che non riesco a scrivere una funzione con argomenti, nella funzione estesa di override, e il compilatore mi dà un errore come questo, ma se scrivo una semplice funzione senza argomenti, e faccio un'implementazione personalizzata e la chiamo con la mia "semplice funzione" sostituita Funziona:

import Foundation 
import UIKit 

extension UIViewController { 

    func slideInView(direction: Direction = Direction.LEFT, duration: CFTimeInterval = 0.5, closure:()->()) { 

     let animation    = CABasicAnimation(keyPath: "transform.translation.x") 
     animation.fromValue   = self.view.bounds.width 
     animation.toValue   = 0 
     animation.duration   = 0.3 
     animation.fillMode   = kCAFillModeForwards; 
     animation.removedOnCompletion = false 

     UIView.animateWithDuration(0.6, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: { 
      self.view!.layer.addAnimation(animation,forKey:nil); 

      }, completion: {(finished) ->() in 
       closure() 
     }); 
    } 

    func slide() { 
     self.slideInView(.LEFT,duration: 0.66) { 
      print("Slide in Left Complete") 
     } 
    } 
} 

class OtherUIViewController: UIViewController { 



    override func slide() { 
     self.slideFromBottom(.BOTTOM,duration: 0.66) { 
      print("Slide in Bottom Complete") 
     } 
    } 

    func slideFromBottom(direction: Direction = Direction.BOTTOM, duration: CFTimeInterval = 0.5, closure:()->()) { 

     let animation    = CABasicAnimation(keyPath: "transform.translation.y") 
     animation.fromValue   = self.view.bounds.height 
     animation.toValue   = 0 
     animation.duration   = 0.3 
     animation.fillMode   = kCAFillModeForwards 
     animation.removedOnCompletion = false 

     UIView.animateWithDuration(0.6, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: { 
      self.view!.layer.addAnimation(animation,forKey:nil); 

      }, completion: {(finished) ->() in 
       closure() 
     }); 
    } 
} 
1

questo è il comportamento di Swift. Può essere buono o cattivo, a seconda delle tue esigenze. Swift usa il dispatch statico, quindi quale metodo è chiamato deve essere conosciuto durante la compilazione. Ci sono alcuni vantaggi e, come al solito, alcuni svantaggi. Per vedere, come funziona Swift al momento attuale, vedere il prossimo esempio molto semplice. Per me sembra logico ...

protocol P { 
    func foo()->Void 
} 
extension P { 
    func foo()->Void { 
     print("protocol foo") 
    } 
} 
class A:P { 
} 
class B:A { 
    func foo() { 
     print("B foo") 
    } 
} 
class C:B { 

} 
class D: C { 
    // here the implementation must be overriden, 
    // due the indirect inheritance from B 
    override func foo() { 
     print("D foo") 
    } 
} 
let a = A()  // a is A type 
a.foo()   // protocol foo 
let b = B()  // B is B type 
b.foo()   // B foo 
let p:P = B() // p is protocol P 
// compiler is not able to know, what i would like, the dynamicType of p 
// can be everything, conforming to protocol P 
p.foo()   // protocol foo 
(p as? A)?.foo() // protocol foo 
// casting to B type, I decided, that p is B type 
(p as? B)?.foo() // B foo 
(p as? D)?.foo() // nothing is printed, becase the result of casting is nil 

// here the types are known at compile time 
let c = C() 
c.foo()   // B foo 
let d = D() 
d.foo()   // D foo 
let e:C = D() 
e.foo()   // D foo