2015-02-19 11 views
7

Ich habe eine Klasse, die an einen Delegaten aufrufen muss, wenn sich eine seiner Eigenschaften ändert. Hier sind die vereinfachte Klasse und das Protokoll für den Delegierten:Swift-Delegat für eine generische Klasse

protocol MyClassDelegate: class { 
    func valueChanged(myClass: MyClass) 
} 

class MyClass { 

    weak var delegate: MyClassDelegate? 

    var currentValue: Int { 
     didSet { 
      if let actualDelegate = delegate { 
       actualDelegate.valueChanged(self) 
      } 
     } 
    } 

    init(initialValue: Int) { 
     currentValue = initialValue 
    } 
} 

Das alles funktioniert gut. Aber ich möchte diese Klasse generisch machen. Also, ich habe es versucht:

protocol MyClassDelegate: class { 
    func valueChanged(genericClass: MyClass) 
} 

class MyClass<T> { 

    weak var delegate: MyClassDelegate? 

    var currentValue: T { 
     didSet { 
      if let actualDelegate = delegate { 
       actualDelegate.valueChanged(self) 
      } 
     } 
    } 

    init(initialValue: T) { 
     currentValue = initialValue 
    } 
} 

Dies wirft zwei Compilerfehler. Zuerst gibt die Zeile, die valueChanged in dem Protokoll deklariert, Reference to generic type 'MyClass' requires arguments in <...>. Zweitens, der Anruf an valueChanged im didSet Watcher wirft: 'MyClassDelegate' does not have a member named 'valueChanged'.

Ich dachte, ein typealias mit würde das Problem lösen:

protocol MyClassDelegate: class { 
    typealias MyClassValueType 
    func valueChanged(genericClass: MyClass<MyClassValueType>) 
} 

class MyClass<T> { 

    weak var delegate: MyClassDelegate? 

    var currentValue: T { 
     didSet { 
      if let actualDelegate = delegate { 
       actualDelegate.valueChanged(self) 
      } 
     } 
    } 

    init(initialValue: T) { 
     currentValue = initialValue 
    } 
} 

ich auf dem richtigen Weg zu sein scheinen, aber ich habe noch zwei Compiler-Fehler. Der zweite Fehler von oben bleibt ebenso wie ein neuer auf der Zeile, die die delegate-Eigenschaft von MyClass deklariert: Protocol 'MyClassDelegate' can only be used as a generic constraint because it has Self or associated type requirements.

Gibt es eine Möglichkeit, dies zu erreichen?

Antwort

23

Es ist schwer zu wissen, was die beste Lösung für Ihr Problem ist, ohne weitere Informationen zu haben, aber eine mögliche Lösung ist die Protokollerklärung zu, dies zu ändern:

protocol MyClassDelegate: class { 
    func valueChanged<T>(genericClass: MyClass<T>) 
} 

, dass die Notwendigkeit für ein typealias entfernt in das Protokoll und sollte die Fehlermeldungen beheben, die Sie bekommen haben.

Teil der Grund, warum ich nicht sicher bin, ob dies die beste Lösung für Sie ist, weil ich nicht weiß, wie oder wo die valueChanged Funktion aufgerufen wird, und so weiß ich nicht, ob es praktisch ist Fügen Sie dieser Funktion einen generischen Parameter hinzu. Wenn diese Lösung nicht funktioniert, poste einen Kommentar.

+0

Das ist genau das, was ich brauchte ... Dank. – GarlicFries

+0

Für das, was es wert ist, wird 'valueChanged' im obigen Code im' didSet' Watcher auf 'currentValue' aufgerufen. – GarlicFries

+0

Große Antwort. Ich konnte einen Delegaten für meine generische Klasse erstellen. Ich habe eine weitere Frage. Wie kann ich herausfinden, welcher Objekt-generische Delegat die generische Delegatfunktion aufruft?Ding1 oder Ding2? Bitte sieh dir diesen Grund an. https://gist.github.com/zakkhoyt/0b2543bb5a5995b41e73bef83391c378 – VaporwareWolf

2

Protokolle können Typanforderungen haben, können aber nicht generisch sein; Protokolle mit Typanforderungen können als generische Einschränkungen verwendet werden, sie können jedoch nicht zum Eingeben von Werten verwendet werden. Aus diesem Grund können Sie Ihren Protokolltyp nicht von Ihrer generischen Klasse referenzieren, wenn Sie diesen Pfad verwenden.

Wenn Ihre Delegation Protokoll ist sehr einfach (wie ein oder zwei Methoden) können Sie Verschlüsse anstelle eines Protokollobjekt akzeptieren:

class MyClass<T> { 
    var valueChanged: (MyClass<T>) -> Void 
} 

class Delegate { 
    func valueChanged(obj: MyClass<Int>) { 
     print("object changed") 
    } 
} 

let d = Delegate() 
let x = MyClass<Int>() 
x.valueChanged = d.valueChanged 

Sie das Konzept auf eine Struktur erweitern können eine Reihe von Schließungen halten:

Seien Sie besonders vorsichtig mit der Speicherverwaltung, da Blöcke einen starken Bezug auf das Objekt haben, auf das sie sich beziehen. Im Gegensatz dazu sind Delegaten typischerweise schwache Referenzen, um Zyklen zu vermeiden.

+0

Funktioniert perfekt, und danke für die Idee der Weitergabe von Schließungen. Roman's Antwort kam zuerst und löst das Problem, das ich aber einfacher habe. – GarlicFries

3

Sie können Vorlagen Methoden mit Typ Löschung verwenden ...

protocol HeavyDelegate : class { 
    func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R 
} 

class Heavy<P, R> { 
    typealias Param = P 
    typealias Return = R 
    weak var delegate : HeavyDelegate? 
    func inject(p : P) -> R? { 
     if delegate != nil { 
      return delegate?.heavy(self, shouldReturn: p) 
     } 
     return nil 
    } 
    func callMe(r : Return) { 
    } 
} 
class Delegate : HeavyDelegate { 
    typealias H = Heavy<(Int, String), String> 

    func heavy<P, R>(heavy: Heavy<P, R>, shouldReturn: P) -> R { 
     let h = heavy as! H // Compile gives warning but still works! 
     h.callMe("Hello") 
     print("Invoked") 
     return "Hello" as! R 
    } 
} 

let heavy = Heavy<(Int, String), String>() 
let delegate = Delegate() 
heavy.delegate = delegate 
heavy.inject((5, "alive")) 
Verwandte Themen