Das Problem ist, dass Sie ein Versprechen abgeben, dass der Compiler nicht beweisen kann, dass Sie es halten werden.
Sie haben dieses Versprechen erstellt: Calling copy()
gibt seinen eigenen Typ zurück, der vollständig initialisiert ist.
Aber dann haben Sie Folgendes implementiert copy()
:
func copy() -> Self {
return C()
}
Jetzt bin ich eine Unterklasse, die nicht überschreibt copy()
. Und ich gebe ein zurück C
, kein vollständig initialisiertes Self
(was ich versprochen habe). Das ist also nicht gut. Wie wäre es mit:
func copy() -> Self {
return Self()
}
Nun, das wird nicht kompiliert, aber selbst wenn es so wäre, wäre es nicht gut. Die Unterklasse hat möglicherweise keinen trivialen Konstruktor und ist daher D()
möglicherweise nicht einmal legal. (Siehe unten.)
OK, wie wäre es mit:
func copy() -> C {
return C()
}
Ja, aber das kommt nicht zurück Self
. Es kehrt zurück C
. Du hältst dein Versprechen immer noch nicht.
"Aber ObjC kann es schaffen!" Naja, so ungefähr. Meistens, weil es egal ist, ob Sie Ihr Versprechen so halten wie Swift. Wenn Sie die Implementierung copyWithZone:
in der Unterklasse nicht durchführen können, können Sie Ihr Objekt möglicherweise nicht vollständig initialisieren. Der Compiler warnt Sie nicht einmal davor, dass Sie das getan haben.
"Aber fast alles in ObjC kann in Swift übersetzt werden, und ObjC hat es NSCopying
." Ja, und so wird es definiert:
func copy() -> AnyObject!
Sie können also dasselbe tun (hier gibt es keinen Grund dafür!):
protocol Copyable {
func copy() -> AnyObject
}
Das heißt "Ich verspreche nichts darüber, was Sie zurückbekommen." Man könnte auch sagen:
protocol Copyable {
func copy() -> Copyable
}
Das ist ein Versprechen, das du machen kannst.
Aber wir können eine Weile über C ++ nachdenken und uns daran erinnern, dass wir ein Versprechen geben können . Wir können versprechen, dass wir und alle unsere Unterklassen bestimmte Arten von Initialisierern implementieren werden, und Swift wird dies durchsetzen (und so beweisen, dass wir die Wahrheit sagen):
protocol Copyable {
init(copy: Self)
}
class C : Copyable {
required init(copy: C) {
}
}
Und so sollten Sie Kopien erstellen.
Wir können noch einen Schritt weiter gehen, aber es wird verwendet dynamicType
, und ich habe es nicht ausführlich getestet, um sicherzustellen, dass dies immer das ist, was wir wollen, aber es sollte korrekt sein:
protocol Copyable {
func copy() -> Self
init(copy: Self)
}
class C : Copyable {
func copy() -> Self {
return self.dynamicType(copy: self)
}
required init(copy: C) {
}
}
Hier versprechen wir, dass es einen Initialisierer gibt, der Kopien für uns ausführt, und dann können wir zur Laufzeit bestimmen, welcher aufgerufen werden soll, und uns die von Ihnen gesuchte Methodensyntax geben.