2016-04-09 9 views
0

Ich definiere ein Protokoll MessageProtocol, und Klasse MessageA, MessageB verabschieden das Protokoll.Swift Schließung Typ Umwandlung

Ich möchte Konvertierungen wie (MessageA) -> Void zu (MessageProtocol) -> Void.

Es gibt Code, Lösung 1 und kann nicht gut funktionieren.

protocol MessageProtocol { 
    func doSomething() 
    static func parserFromData() -> MessageProtocol 
} 

class MessageA: MessageProtocol { 
    func doSomething() { 
     print("this is A class") 
    } 

    func functionInA() { 
     print("funciton in A") 
    } 

    static func parserFromData() -> MessageProtocol { 
     return MessageA() 
    } 
} 

class MessageB: MessageProtocol { 
    func doSomething() { 
     print("this is B class") 
    } 

    func functionInB() { 
     print("funciton in b") 
    } 

    static func parserFromData() -> MessageProtocol { 
     return MessageB() 
    } 
} 

// private function .network callback 
func callback(type: MessageProtocol.Type, handler: (MessageProtocol?) -> Void) -> Void { 
    let obj = type.parserFromData() 
    handler(obj) 
} 

// public function 
func response<T: MessageProtocol>(type: T.Type, handler: (T?) -> Void) -> Void { 

    // solution 1. compile error 
// let handler: (ABProtocol) -> Void = handler  // compile error 
// callback(type, handler: handler) 

    // solution 2. runtime error 
// let handler = handler as! (ABProtocol) -> Void // runtime error 
// callback(type, handler: handler) 

    // solution 3. now i use 
    callback(type) { obj in 
     handler(obj as? T) 
    } 
} 

response(MessageA.self) { a in 
    guard let a = a else { 
     return 
    } 
    a.doSomething() 
    a.functionInA() 
} 

response(MessageB.self) { b in 
    guard let b = b else { 
     return 
    } 
    b.doSomething() 
    b.functionInB() 
} 
+0

Ich bin verwirrt, was Sie eigentlich versuchen, und warum die Arbeitslösung nicht für Sie arbeitet. Scheint so, als ob dies ein XY-Problem sein könnte, basierend auf der vollkommenen Non-Straight-Fowardness des Problems. – nhgrif

+0

meine Frage ist 'MessageA' kann als' MessageProtocol' konvergieren, aber warum conver '(MessageA) -> Void' als' (MessageProtocol) -> Void' wird fehlschlagen. – langyanduan

Antwort

0

einige Protokoll und konkrete Klassen Da das Protokoll der Umsetzung, werden Sie nicht in der Lage sein, eine Schließung Argument aus der Klasse auf das Protokoll upCast.

Um klar zu sein, da diese Hierarchie von Protokollen und Klassen:

var firstClosure: (FirstClass) -> Void 

bis zu dieser Schließung:

var fooClosure = firstClosure as (FirstClass) -> Void 

protocol FooProtocol {} 

class FirstClass: FooProtocol {} 
class SecondClass: FooProtocol {} 

Wir werden nie diese Schließung werfen können,

Trotz der Tatsache, dass der Compiler perfekt glücklich wäre, lassen Sie uns Variablen der Klasse selbst zu werfen. Zum Beispiel:

let first: FirstClass = FirstClass() 
let foo = first as FooProtocol 

Es braucht nicht einmal ein as? oder as! zu sein. Der Compiler weiß, dass FirstClass das Protokoll implementiert und dies wird immer passieren.

Das Problem mit Verschlüssen sind wir Gießen nicht nur das Arguments - sie waren versucht, die ganze Schließung zu werfen.

sollten diesem Beispiel identifizieren, warum der Compiler über die Besetzung beschweren:

protocol DoThingProtocol { 
    func doThing() 
} 

class FooClass: DoThingProtocol { 
    var shouldDoThing = false 
    func doThing() { 
     print("Foo did the thing") 
    } 
} 

class BarClass: DoThingProtocol { 
    func doThing() { 
     print("Bar did the thing") 
    } 
} 

Nun nehmen wir an, wir erstellen einen Verschluss nehmen ein FooClass: ist

let fooClosure: (FooClass) -> Void = { thing in 
    if thing.shouldDoThing { 
     thing.doThing() 
    } 
} 

Der Verschluss definiert als Nehmen eine FooClass und diese konkrete Schließung behandelt das Argument als eine FooClass Instanz - es greift auf die shouldDoThing Eigenschaft von FooClass, die nicht defi ist ned on BarClass, trotz der Tatsache, dass BarClass auch die DoThingProtocol implementiert.

Da der Verschluss zu behandeln, wird die als FooClass und potenziell Zugang Teile des Arguments in Argument übergeben, die nur in FooClass und nichts implementiert, die FooClass erbt von, nur FooClass und seine Nachkommen um die passieren kann Schließung. Du kannst nichts weiter oben in der Kette oder Geschwister einbringen.

Und um klar zu sein, ist dies nicht auf Protokolle beschränkt. Wenn DoThingProtocol wo statt DoThingClass und eine Klasseninstanz, die FooClass & BarClass aus geerbt, würden wir uns genau in der gleichen Situation befinden.

Wenn Sie in der Lage sein wollen beide Instanzen von FooClass passieren und BarClass, Ihre beste Wette ist die Schließung als Einnahme DoThingProtocol zu definieren.

Für was es wert ist, können Sie Ihre Schließungen downcast. Betrachten Sie diese sehr einfaches Beispiel:

class A {} 
class B: A {} 

let closureA: (A) -> Void = { arg in 
    print(arg) 
} 

let closureB = closureA as (B) -> Void 

In diesem Fall wird B garantiert alles zu implementieren, die A umgesetzt hat, weil B erbt von A. Es gibt nicht unbedingt eine Menge Zweck, um diese Besetzung zu machen. Der Cast beschränkt nur die anderen untergeordneten Klassen von A auf closureB, aber da der Abschluss bereits als Typ (A) -> Void implementiert wurde, können wir kein zusätzliches Wissen über die Klasse B innerhalb des Closings verwenden.