2017-06-12 5 views
0

Ich habe bereits eine question geschrieben, aber es war nicht klar, was ich will. Wie @AlainT vorgeschlagen hat, habe ich einen neuen eingereicht.Generic View Controller wird nicht mit Delegaten und Erweiterungen

Ich habe eine typealias Tupel

public typealias MyTuple<T> = (key: T, value: String) 

Ein Protokoll:

public protocol VCADelegate: class { 
    associatedtype T 
    func didSelectData(_ selectedData: MyTuple<T>) 
} 

A-View-Controller (VCA) mit einer Tabellenansicht

class VCA<T>: UIViewController, UITableViewDelegate, UITableViewDataSource { 

    var dataList = [MyTuple<T>]() 
    weak var delegate: VCADelegate? // Error: can only be used as a generic constraint  

    // ... 

    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     delegate?.didSelectData(dataList[indexPath.row]) 
    } 
} 

in einer anderen Ansicht Regler (VCB) Ich erstelle eine VCA und durchlaufe eine DataList

func callVCA() { 
    let vcA = VCA<String>() 
    vcA.dataList = [(key: "1", value:"Value 1"), 
        (key: "2", value:"Value 2")] 
} 

Ich möchte eine DataList haben, ohne den Datentyp des Schlüssels in VCA zu kennen. Nur wenn VCB VCA aufruft, kenne ich den Datentyp des Schlüssels. Das Erstellen eines generischen View-Controllers führt zu einem Problem mit dem Delegaten. Gibt es eine Möglichkeit, dieses Problem zu lösen, ohne zum Abschluss der Schließung wechseln zu müssen?

Das andere Problem der Verwendung eines generischen View-Controllers ist, dass ich es nicht erweitern kann. Irgendeine Idee?

+0

"Was ich will Zu tun ist, eine Datenliste zu haben, ohne den Datentyp des Schlüssels in VCA zu kennen. Nur wenn VCB VCA aufruft, dann kenne ich den Datentyp des Schlüssels. " Es ist unklar, was du hier meinst. Wenn VCB den zugehörigen VCA-Typ kennt (um ihn zu nennen), wie Sie es sagen, woher kennt er dann den Typ nicht? Was ist das "Problem mit dem Delegierten?" Nichts hier scheint VCA als eine Eigenschaft zu speichern; Ist es nur eine lokale Variable? (Wenn ja, was ist der aufrufende Code? Es scheint, dass Sie T == String hart codiert haben, also wo weißt du das nicht?) –

+0

Das Problem mit dem Delegaten ist in meinem Kommentar richtig: "kann nur als verwendet werden eine generische Einschränkung ... " – Lawliet

+0

Was ich meine, ist in VCA, ich weiß nicht, Datentyp von MyTuple Schlüssel. Nur VCB kennt es. Von VCB konnte ich entweder String, Int oder einen anderen Datentyp an den MyTuple-Schlüssel übergeben. "Nichts scheint hier VCA als Eigenschaft zu speichern", wenn Sie den letzten Codeblock nicht verstehen. Ich habe es in eine Funktion gebracht. – Lawliet

Antwort

1

Dies ist eine Standard-Typ-Lösch-Situation, obwohl ich in diesem speziellen Fall nur eine Schließung passieren würde (da es nur eine Methode gibt).

eine Art Radiergummi erstellen anstelle eines Protokolls:

public struct AnyVCADelegate<T> { 
    let _didSelectData: (MyTuple<T>) -> Void 
    func didSelectData(_ selectedData: MyTuple<T>) { _didSelectData(selectedData)} 
    init<Delegate: VCADelegate>(delegate: Delegate) where Delegate.T == T { 
     _didSelectData = delegate.didSelectData 
    } 
} 

verwenden, die anstelle eines Delegierten:

class VCA<T>: UIViewController, UITableViewDataSource UITableViewDelegate { 

    var dataList = [MyTuple<T>]() 
    var delegate: AnyVCADelegate<T>? 

    // ... 

    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     delegate?.didSelectData(dataList[indexPath.row]) 
    } 
} 

Ihr zugrunde liegende Problem ist, dass die Protokolle mit den dazugehörigen Typen sind nicht die richtige Art selbst. Sie sind nur Typenbeschränkungen. Wenn Sie es ein PAT behalten wollen, das ist in Ordnung, aber dann muss man VCA generic über den Delegierten machen:

class VCA<Delegate: VCADelegate>: UIViewController, UITableViewDelegate { 

    var dataList = [MyTuple<Delegate.T>]() 
    weak var delegate: Delegate? 

    init(delegate: Delegate?) { 
     self.delegate = delegate 
     super.init(nibName: nil, bundle: nil) 
    } 

    required init(coder: NSCoder) { super.init(nibName: nil, bundle: nil) } 

    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     delegate?.didSelectData(dataList[indexPath.row]) 
    } 
} 

class VCB: UIViewController, VCADelegate { 

    func didSelectData(_ selectedData: MyTuple<String>) {} 

    func callVCA() { 
     let vcA = VCA(delegate: self) 
     vcA.dataList = [(key: "1", value:"Cinnamon"), 
         (key: "2", value:"Cloves")] 
    } 
} 

In der Regel Protokolle mit den dazugehörigen Typen (PATs) sind ein sehr mächtiges, aber Special- Zweck-Tool. Sie sind kein Ersatz für Generika (die ein Allzweck-Tool sind).

Für dieses spezielle Problem würde ich wahrscheinlich nur eine Schließung passieren. Alles ein Radiergummi ist (normalerweise) eine Struktur, die mit Verschlüssen gefüllt ist. (Irgendwann wird der Compiler wahrscheinlich nur für uns schreiben, und ein Großteil dieses Problems wird verschwinden und PATs werden im täglichen Code nützlich sein, aber für den Moment tut es das nicht.)

+0

Danke Rob für die ausführliche Antwort.Ich versuche, meinen Kopf um die Art des Löschens zu wickeln, werde zurückkommen, um deine Antwort noch einmal zu überprüfen. – Lawliet

+0

Ein paar Ressourcen: http://robnapier.net/erasure führt Typ Radiergummis. https://www.dotconferences.com/2016/01/rob-napier-beyond-crusty-real-world-protocols Sprechen Sie über das Ersetzen komplexer Protokolle durch einfachere Strukturen. –

+0

Tolles Gespräch, danke. – Lawliet

Verwandte Themen