Beim Spielen mit Swift, kommend aus einem Java-Hintergrund, warum sollte man sich für eine Struktur statt einer Klasse entscheiden? Es scheint, als ob sie dasselbe wären, wobei eine Struktur weniger Funktionalität bietet. Warum also wählen?
Antworten
Zu viele Anzeigen?Ich würde nicht sagen, dass Strukturen weniger Funktionalität bieten.
Sicher, self ist unveränderlich, außer in einer mutating Funktion, aber das ist auch schon alles.
Vererbung funktioniert gut, solange man sich an die gute alte Idee hält, dass jede Klasse entweder abstrakt oder final sein sollte.
Implementieren Sie abstrakte Klassen als Protokolle und finale Klassen als Strukturen.
Das Schöne an Strukturen ist, dass Sie Ihre Felder veränderlich machen können, ohne gemeinsamen veränderlichen Zustand zu erzeugen, denn Copy-On-Write kümmert sich darum :)
Deshalb sind die Eigenschaften / Felder im folgenden Beispiel alle veränderlich, was ich in Java oder C# oder swift Klassen nicht machen würde.
Beispiel Vererbungsstruktur mit etwas schmutziger und direkter Verwendung unten in der Funktion "Beispiel":
protocol EventVisitor
{
func visit(event: TimeEvent)
func visit(event: StatusEvent)
}
protocol Event
{
var ts: Int64 { get set }
func accept(visitor: EventVisitor)
}
struct TimeEvent : Event
{
var ts: Int64
var time: Int64
func accept(visitor: EventVisitor)
{
visitor.visit(self)
}
}
protocol StatusEventVisitor
{
func visit(event: StatusLostStatusEvent)
func visit(event: StatusChangedStatusEvent)
}
protocol StatusEvent : Event
{
var deviceId: Int64 { get set }
func accept(visitor: StatusEventVisitor)
}
struct StatusLostStatusEvent : StatusEvent
{
var ts: Int64
var deviceId: Int64
var reason: String
func accept(visitor: EventVisitor)
{
visitor.visit(self)
}
func accept(visitor: StatusEventVisitor)
{
visitor.visit(self)
}
}
struct StatusChangedStatusEvent : StatusEvent
{
var ts: Int64
var deviceId: Int64
var newStatus: UInt32
var oldStatus: UInt32
func accept(visitor: EventVisitor)
{
visitor.visit(self)
}
func accept(visitor: StatusEventVisitor)
{
visitor.visit(self)
}
}
func readEvent(fd: Int) -> Event
{
return TimeEvent(ts: 123, time: 56789)
}
func example()
{
class Visitor : EventVisitor
{
var status: UInt32 = 3;
func visit(event: TimeEvent)
{
print("Ein Zeitereignis: \(event)")
}
func visit(event: StatusEvent)
{
print("Ein Statusereignis: \(event)")
if let change = event as? StatusChangedStatusEvent
{
status = change.newStatus
}
}
}
let visitor = Visitor()
readEvent(1).accept(visitor)
print("status: \(visitor.status)")
}
Mit Klassen erhalten Sie Vererbung und werden per Referenz übergeben, Strukturen haben keine Vererbung und werden per Wert übergeben.
Es gibt großartige WWDC-Sitzungen zu Swift, diese spezifische Frage wird in einer von ihnen ausführlich beantwortet. Achten Sie darauf, diese anzusehen, da Sie dadurch viel schneller auf den neuesten Stand kommen als mit dem Sprachführer oder dem iBook.
In Swift wurde ein neues Programmiermuster namens protokollorientiertes Programmieren eingeführt.
Erstellungsmuster:
In Swift, Struct ist ein Wertetyp, der automatisch geklont wird. Daher erhalten wir das erforderliche Verhalten, um das Prototypenmuster kostenlos zu implementieren.
Während Klassen der Referenztyp sind, die nicht automatisch beim Zuweisen geklont werden. Um das Prototypenmuster zu implementieren, müssen Klassen das NSCopying
-Protokoll übernehmen.
Shallow Copy dupliziert nur die Referenz, die auf diese Objekte zeigt, während Deep Copy die Referenz des Objekts dupliziert.
Das Implementieren einer Deep Copy für jeden Referenztyp ist zu einer mühsamen Aufgabe geworden. Wenn Klassen weitere Referenztypen enthalten, müssen wir das Prototypenmuster für jede der referenzierten Eigenschaften implementieren. Und dann müssen wir tatsächlich den gesamten Objektgraphen kopieren, indem wir das NSCopying
-Protokoll implementieren.
class Contact{
var firstName:String
var lastName:String
var workAddress:Address // Referenztyp
}
class Address{
var street:String
...
}
Durch die Verwendung von Strukturen und Enums haben wir unseren Code einfacher gemacht, da wir die Kopierlogik nicht implementieren müssen.
Ein Punkt, der in diesen Antworten nicht beachtet wird, ist, dass eine Variable, die eine Klasse gegenüber einer Struktur hält, ein let
sein kann, während immer noch Änderungen an den Eigenschaften des Objekts möglich sind, während dies bei einer Struktur nicht möglich ist.
Dies ist nützlich, wenn Sie nicht möchten, dass die Variable jemals auf ein anderes Objekt verweist, aber dennoch das Objekt ändern müssen, z.B. im Fall von vielen Instanzvariablen, die Sie nacheinander aktualisieren möchten. Wenn es sich um eine Struktur handelt, müssen Sie die Variable auf ein anderes Objekt zurücksetzen können, indem Sie var
verwenden, um dies zu erreichen, da ein konstanter Werttyp in Swift nullmutation korrekt zulässt, während Verweistypen (Klassen) sich nicht auf diese Weise verhalten.