Es gibt eine weitere Lösung, die in dieser Frage nicht erwähnt wurde, nämlich die Verwendung einer Technik namens Typlöschung . Um eine abstrakte Schnittstelle für ein generisches Protokoll zu erhalten, erstellen Sie eine Klasse oder Struktur, die ein Objekt oder eine Struktur umschließt, die dem Protokoll entspricht. Die Wrapper-Klasse, normalerweise 'Any {Protokollname}' genannt, entspricht selbst dem Protokoll und implementiert seine Funktionen, indem alle Aufrufe an das interne Objekt weitergeleitet werden. Probieren Sie das folgende Beispiel auf einem Spielplatz aus:
import Foundation
public protocol Printer {
typealias T
func print(val:T)
}
struct AnyPrinter<U>: Printer {
typealias T = U
private let _print: U -> ()
init<Base: Printer where Base.T == U>(base : Base) {
_print = base.print
}
func print(val: T) {
_print(val)
}
}
struct NSLogger<U>: Printer {
typealias T = U
func print(val: T) {
NSLog("\(val)")
}
}
let nsLogger = NSLogger<Int>()
let printer = AnyPrinter(base: nsLogger)
printer.print(5) // prints 5
Der Typ von printer
ist bekannt AnyPrinter<Int>
und kann verwendet werden, um jede mögliche Implementierung des Druckerprotokolls zu abstrahieren. Obwohl AnyPrinter technisch nicht abstrakt ist, ist seine Implementierung nur ein Durchfall zu einem echten Implementierungstyp und kann verwendet werden, um Implementierungstypen von den Typen zu entkoppeln, die sie verwenden.
Zu beachten ist, dass die AnyPrinter
Basisinstanz nicht explizit beibehalten werden muss. In der Tat können wir nicht, da wir nicht erklären können AnyPrinter
, eine Printer<T>
Immobilie zu haben . Stattdessen erhalten wir einen Funktionszeiger _print
auf die print
Funktion der Basis . Aufruf base.print
ohne es den Aufruf gibt eine Funktion , wo Base als Selbst Variable curried wird und auf diese Weise für zukünftige Anrufungen beibehalten.
Eine andere Sache, die Sie beachten sollten, ist, dass diese Lösung im Wesentlichen eine weitere Schicht des dynamischen Versands darstellt, was einen leichten Leistungseinbruch bedeutet. Außerdem benötigt die Instanz zum Löschen von Typen zusätzlichen Speicher über der zugrunde liegenden Instanz. Aus diesen Gründen ist das Löschen von Typen keine kostenlose Abstraktion.
Natürlich gibt es einige Arbeiten zum Einrichten der Typlöschung, aber es kann sehr nützlich sein, wenn eine generische Protokollabstraktion erforderlich ist. Dieses Muster befindet sich in der schnellen Standardbibliothek mit Typen wie AnySequence
. Weiterführende Literatur: http://robnapier.net/erasure
BONUS:
Wenn Sie entscheiden, dass Sie Printer
überall dieselbe Implementierung injizieren möchten , können Sie einen praktischen Initialisierer bereitstellen, für AnyPrinter
den dieser Typ injiziert wird.
extension AnyPrinter {
convenience init() {
let nsLogger = NSLogger<T>()
self.init(base: nsLogger)
}
}
let printer = AnyPrinter<Int>()
printer.print(10) //prints 10 with NSLog
Dies kann eine einfache und trockene Methode sein, um Abhängigkeitsinjektionen für Protokolle auszudrücken, die Sie in Ihrer App verwenden.