Wenn Sie mit Swift aus Java spielen, warum sollten Sie eine Struktur anstelle einer Klasse wählen? Es scheint, als wären sie dasselbe, da eine Struktur weniger Funktionalität bietet. Warum dann wählen?
swift
class
struct
design-principles
bluedevil2k
quelle
quelle
Antworten:
Laut dem sehr beliebten WWDC 2015 Talk Protocol Oriented Programming in Swift ( Video , Transkript ) bietet Swift eine Reihe von Funktionen, mit denen Strukturen unter vielen Umständen besser als Klassen sind.
Strukturen sind vorzuziehen, wenn sie relativ klein und kopierbar sind, da das Kopieren viel sicherer ist als mehrere Verweise auf dieselbe Instanz wie bei Klassen. Dies ist besonders wichtig, wenn eine Variable an viele Klassen und / oder in einer Multithread-Umgebung weitergegeben wird. Wenn Sie immer eine Kopie Ihrer Variablen an andere Orte senden können, müssen Sie sich keine Sorgen machen, dass dieser andere Ort den Wert Ihrer Variablen unter Ihnen ändert.
Mit Structs müssen Sie sich weniger Gedanken über Speicherverluste oder mehrere Threads machen, die auf eine einzelne Instanz einer Variablen zugreifen oder diese ändern möchten. (Für technisch Interessierte besteht die Ausnahme darin, dass eine Struktur innerhalb eines Abschlusses erfasst wird, da dann tatsächlich ein Verweis auf die Instanz erfasst wird, es sei denn, Sie markieren ihn ausdrücklich als zu kopierend.)
Klassen können auch aufgebläht werden, da eine Klasse nur von einer einzelnen Oberklasse erben kann. Das ermutigt uns, riesige Superklassen zu erstellen, die viele verschiedene Fähigkeiten umfassen, die nur lose miteinander verbunden sind. Durch die Verwendung von Protokollen, insbesondere mit Protokollerweiterungen, bei denen Sie Implementierungen für Protokolle bereitstellen können, können Sie die Notwendigkeit von Klassen beseitigen, um diese Art von Verhalten zu erreichen.
Der Vortrag beschreibt diese Szenarien, in denen Klassen bevorzugt werden:
Dies impliziert, dass Strukturen die Standardeinstellung sein sollten und Klassen ein Fallback sein sollten.
Andererseits ist die Dokumentation zur Swift-Programmiersprache etwas widersprüchlich:
Hier wird behauptet, dass wir standardmäßig Klassen verwenden und Strukturen nur unter bestimmten Umständen verwenden sollten. Letztendlich müssen Sie die realen Auswirkungen von Werttypen im Vergleich zu Referenztypen verstehen und dann eine fundierte Entscheidung darüber treffen, wann Strukturen oder Klassen verwendet werden sollen. Denken Sie auch daran, dass sich diese Konzepte ständig weiterentwickeln und die Dokumentation zur Swift Programming Language geschrieben wurde, bevor der Vortrag über die protokollorientierte Programmierung gehalten wurde.
quelle
In practice, this means that most custom data constructs should be classes, not structures.
Können Sie mir erklären, wie Sie nach dem Lesen erhalten, dass die meisten Datensätze Strukturen und keine Klassen sein sollten? Sie gaben ein bestimmtes Regelwerk an, wenn etwas eine Struktur sein sollte, und sagten so ziemlich "alle anderen Szenarien, in denen eine Klasse besser ist".Da Strukturinstanzen auf dem Stapel und Klasseninstanzen auf dem Heap zugewiesen werden, können Strukturen manchmal drastisch schneller sein.
Sie sollten es jedoch immer selbst messen und anhand Ihres speziellen Anwendungsfalls entscheiden.
Betrachten Sie das folgende Beispiel, das zwei Strategien zum Umschließen des
Int
Datentyps mitstruct
und demonstriertclass
. Ich verwende 10 wiederholte Werte, um die reale Welt, in der Sie mehrere Felder haben, besser widerzuspiegeln.Die Leistung wird mit gemessen
Code finden Sie unter https://github.com/knguyen2708/StructVsClassPerformance
UPDATE (27. März 2018) :
Ab Swift 4.0, Xcode 9.2, mit Release Build auf iPhone 6S, iOS 11.2.6, ist die Swift Compiler-Einstellung
-O -whole-module-optimization
:class
Version dauerte 2,06 Sekundenstruct
Version dauerte 4.17e-08 Sekunden (50.000.000 mal schneller)(Ich habe keine durchschnittlichen Mehrfachläufe mehr, da die Abweichungen mit unter 5% sehr gering sind.)
Hinweis : Der Unterschied ist ohne Optimierung des gesamten Moduls viel weniger dramatisch. Ich würde mich freuen, wenn jemand darauf hinweisen kann, was die Flagge tatsächlich tut.
UPDATE (7. Mai 2016) :
Ab Swift 2.2.1, Xcode 7.3, mit Release Build auf iPhone 6S, iOS 9.3.1, gemittelt über 5 Läufe, lautet die Swift Compiler-Einstellung
-O -whole-module-optimization
:class
Version dauerte 2.159942142sstruct
Version dauerte 5.83E-08s (37.000.000 mal schneller)Hinweis : Da jemand erwähnt hat, dass in realen Szenarien wahrscheinlich mehr als ein Feld in einer Struktur vorhanden ist, habe ich Tests für Strukturen / Klassen mit 10 statt 1 Feldern hinzugefügt. Überraschenderweise variieren die Ergebnisse nicht stark.
URSPRÜNGLICHE ERGEBNISSE (1. Juni 2014):
(Lief auf Struktur / Klasse mit 1 Feld, nicht 10)
Ab Swift 1.2, Xcode 6.3.2, mit Release Build auf iPhone 5S, iOS 8.3, durchschnittlich über 5 Läufe
class
Version dauerte 9.788332333sstruct
Version dauerte 0.010532942s (900 mal schneller)ALTE ERGEBNISSE (aus unbekannter Zeit)
(Lief auf Struktur / Klasse mit 1 Feld, nicht 10)
Mit Release Build auf meinem MacBook Pro:
class
Version dauerte 1.10082 Sekundenstruct
Version dauerte 0,02324 Sekunden (50-mal schneller)quelle
Ähnlichkeiten zwischen Strukturen und Klassen.
Ich habe das Wesentliche dafür mit einfachen Beispielen erstellt. https://github.com/objc-swift/swift-classes-vs-structures
Und Unterschiede
1. Vererbung.
Strukturen können nicht schnell erben. Falls Sie es wollen
Geh in eine Klasse.
2. Vorbeifahren
Schnelle Strukturen werden als Wert und Klasseninstanzen als Referenz übergeben.
Kontextunterschiede
Strukturkonstante und Variablen
Beispiel (verwendet auf der WWDC 2014)
Definiert eine Struktur namens Point.
Nun, wenn ich versuche, das x zu ändern. Es ist ein gültiger Ausdruck.
Aber wenn ich einen Punkt als konstant definiert habe.
In diesem Fall ist der gesamte Punkt unveränderlich konstant.
Wenn ich stattdessen einen Klassenpunkt verwendet habe, ist dies ein gültiger Ausdruck. Weil in einer Klasse unveränderliche Konstante der Verweis auf die Klasse selbst nicht ihre Instanzvariablen ist (es sei denn, diese Variablen sind als Konstanten definiert)
quelle
Hier sind einige andere Gründe zu berücksichtigen:
Strukturen erhalten einen automatischen Initialisierer, den Sie überhaupt nicht im Code pflegen müssen.
Um dies in einer Klasse zu erhalten, müssten Sie den Initialisierer hinzufügen und den Intializer beibehalten ...
Grundlegende Sammlungstypen wie
Array
sind Strukturen. Je häufiger Sie sie in Ihrem eigenen Code verwenden, desto mehr werden Sie sich daran gewöhnen, Werte anstelle von Referenzen zu übergeben. Zum Beispiel:Anscheinend ist Unveränderlichkeit vs. Veränderlichkeit ein großes Thema, aber viele kluge Leute halten Unveränderlichkeit - in diesem Fall Strukturen - für vorzuziehen. Veränderliche gegen unveränderliche Objekte
quelle
internal
Gültigkeitsbereichs verfügbar ist .mutating
damit Sie explizit angeben, welche Funktionen ihren Status ändern. Aber ihre Natur als Werttyp ist das, was wichtig ist. Wenn Sie eine Struktur mit deklarierenlet
, können Sie keine mutierenden Funktionen darauf aufrufen. Das WWDC 15-Video zur besseren Programmierung durch Werttypen ist hierfür eine hervorragende Ressource.Unter der Annahme , dass wir wissen , Struct ist ein Werttyp und Klasse ist ein Referenztyp .
Wenn Sie nicht wissen, was ein Werttyp und ein Referenztyp sind, lesen Sie Was ist der Unterschied zwischen Referenzübergabe und Wertübergabe?
Basierend auf Mikeashs Beitrag :
Ich persönlich benenne meine Klassen nicht so. Normalerweise nenne ich meinen UserManager anstelle von UserController, aber die Idee ist dieselbe
Verwenden Sie außerdem keine Klasse, wenn Sie jede einzelne Instanz einer Funktion überschreiben müssen, dh sie haben keine gemeinsame Funktionalität.
Also anstatt mehrere Unterklassen einer Klasse zu haben. Verwenden Sie mehrere Strukturen, die einem Protokoll entsprechen.
Ein weiterer vernünftiger Fall für Strukturen ist, wenn Sie ein Delta / Diff Ihres alten und neuen Modells erstellen möchten. Mit Referenztypen können Sie dies nicht sofort tun. Bei Werttypen werden die Mutationen nicht gemeinsam genutzt.
quelle
Einige Vorteile:
quelle
Struktur ist viel schneller als Klasse. Wenn Sie eine Vererbung benötigen, müssen Sie außerdem Class verwenden. Der wichtigste Punkt ist, dass Klasse ein Referenztyp ist, während Struktur ein Werttyp ist. zum Beispiel,
Jetzt können Sie eine Instanz von beiden erstellen.
Lassen Sie uns diese Instanz nun an zwei Funktionen übergeben, die die ID, Beschreibung, das Ziel usw. ändern.
ebenfalls,
damit,
Wenn wir nun die ID und Beschreibung des Flugs A ausdrucken, erhalten wir
Hier können wir sehen, dass die ID und Beschreibung von FlightA geändert wird, da der an die Änderungsmethode übergebene Parameter tatsächlich auf die Speicheradresse des FlightA-Objekts (Referenztyp) verweist.
Wenn wir nun die ID und Beschreibung der FLightB-Instanz drucken, erhalten wir:
Hier können wir sehen, dass die FlightB-Instanz nicht geändert wird, da in der Methode "modifyFlight2" die tatsächliche Instanz von Flight2 übergeben wird und nicht die Referenz (Werttyp).
quelle
Here we can see that the FlightB instance is not changed
Structs
sindvalue type
undClasses
sindreference type
Verwenden Sie einen
value
Typ, wenn:Benutze einen
reference
Typ, wenn:Weitere Informationen finden Sie auch in der Apple-Dokumentation
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
zusätzliche Information
Schnelle Werttypen werden im Stapel beibehalten. In einem Prozess verfügt jeder Thread über einen eigenen Stapelbereich, sodass kein anderer Thread direkt auf Ihren Wertetyp zugreifen kann. Daher keine Race-Bedingungen, Sperren, Deadlocks oder damit verbundene Komplexität der Thread-Synchronisation.
Werttypen benötigen keine dynamische Speicherzuweisung oder Referenzzählung. Beides sind teure Vorgänge. Gleichzeitig werden Methoden zu Werttypen statisch ausgeliefert. Diese schaffen einen großen Vorteil zugunsten von Werttypen in Bezug auf die Leistung.
Zur Erinnerung hier eine Liste von Swift
Werttypen:
Referenztypen:
quelle
Die Beantwortung der Frage aus der Perspektive von Werttypen und Referenztypen in diesem Apple-Blogbeitrag erscheint sehr einfach:
Wie in diesem Artikel erwähnt, verhält sich eine Klasse ohne beschreibbare Eigenschaften identisch mit einer Struktur, mit (ich werde hinzufügen) einer Einschränkung: Strukturen eignen sich am besten für threadsichere Modelle - eine zunehmend bevorstehende Anforderung in der modernen App-Architektur.
quelle
Bei Klassen erhalten Sie eine Vererbung und werden als Referenz übergeben. Strukturen haben keine Vererbung und werden als Wert übergeben.
Es gibt großartige WWDC-Sitzungen zu Swift, diese spezielle Frage wird in einer von ihnen ausführlich beantwortet. Achten Sie darauf, dass Sie sich diese ansehen, da Sie dadurch viel schneller auf den neuesten Stand gebracht werden als im Sprachhandbuch oder im iBook.
quelle
Ich würde nicht sagen, dass Strukturen weniger Funktionalität bieten.
Sicher, Selbst ist unveränderlich, außer in einer mutierenden Funktion, aber das war es auch schon.
Vererbung funktioniert gut, solange Sie an der guten alten Idee festhalten, dass jede Klasse entweder abstrakt oder endgültig sein sollte.
Implementieren Sie abstrakte Klassen als Protokolle und endgültige Klassen als Strukturen.
Das Schöne an Strukturen ist, dass Sie Ihre Felder veränderbar machen können, ohne einen gemeinsamen veränderlichen Status zu erstellen, da das Kopieren beim Schreiben dafür sorgt :)
Aus diesem Grund sind die Eigenschaften / Felder im folgenden Beispiel alle veränderbar, was ich in Java- oder C # - oder Swift- Klassen nicht tun würde .
Beispiel für eine Vererbungsstruktur mit etwas schmutziger und unkomplizierter Verwendung unten in der Funktion "Beispiel":
quelle
Schöpfungsmuster:
In schnellen, ist Struct ein Werttyp , die automatisch geklont werden. Daher erhalten wir das erforderliche Verhalten, um das Prototypmuster kostenlos zu implementieren.
Während Klassen der Referenztyp sind, der während der Zuweisung nicht automatisch geklont wird. Um das Prototypmuster zu implementieren, müssen Klassen das
NSCopying
Protokoll übernehmen.Flache Kopie dupliziert nur die Referenz, die auf diese Objekte verweist, während tiefe Kopie die Referenz des Objekts dupliziert.
Das Implementieren einer tiefen Kopie für jeden Referenztyp ist zu einer mühsamen Aufgabe geworden. Wenn Klassen weitere Referenztypen enthalten, müssen wir für jede der Referenzeigenschaften ein Prototypmuster implementieren. Und dann müssen wir tatsächlich den gesamten Objektgraphen kopieren, indem wir das
NSCopying
Protokoll implementieren .Durch die Verwendung von Strukturen und Aufzählungen haben wir unseren Code einfacher gemacht, da wir die Kopierlogik nicht implementieren müssen.
quelle
Viele Cocoa-APIs erfordern NSObject-Unterklassen, wodurch Sie zur Verwendung von Klassen gezwungen werden. Ansonsten können Sie anhand der folgenden Fälle aus dem Swift-Blog von Apple entscheiden, ob Sie einen Struktur- / Aufzählungswerttyp oder einen Klassenreferenztyp verwenden möchten.
https://developer.apple.com/swift/blog/?id=10
quelle
Ein Punkt, der in diesen Antworten nicht beachtet wird, ist, dass eine Variable, die eine Klasse gegen eine Struktur enthält, noch eine
let
Weile Änderungen an den Eigenschaften des Objekts zulässt, während Sie dies mit einer Struktur nicht tun können.Dies ist nützlich, wenn Sie nicht möchten, dass die Variable jemals auf ein anderes Objekt zeigt, aber dennoch das Objekt ändern müssen, dh wenn Sie viele Instanzvariablen haben, die Sie nacheinander aktualisieren möchten. Wenn es sich um eine Struktur handelt, müssen Sie zulassen, dass die Variable insgesamt auf ein anderes Objekt zurückgesetzt wird
var
, da ein konstanter Werttyp in Swift eine Nullmutation ordnungsgemäß zulässt, während sich Referenztypen (Klassen) nicht so verhalten.quelle
Da struct Werttypen sind und Sie den Speicher sehr einfach erstellen können, der im Stapel gespeichert wird. Struktur kann leicht zugänglich sein und nach dem Umfang der Arbeit kann die Zuordnung vom Stapelspeicher durch Pop von der Oberseite des Stapels leicht aufgehoben werden. Andererseits ist die Klasse ein Referenztyp, der im Heap gespeichert wird, und Änderungen, die in einem Klassenobjekt vorgenommen werden, wirken sich auf andere Objekte aus, da diese eng miteinander verbunden sind und den Referenztyp. Alle Mitglieder einer Struktur sind öffentlich, während alle Mitglieder einer Klasse privat sind .
Die Nachteile von struct sind, dass es nicht vererbt werden kann.
quelle
Struktur und Klasse sind benutzerdefinierte Datentypen
Standardmäßig ist die Struktur öffentlich, während die Klasse privat ist
Klasse implementiert das Prinzip der Kapselung
Objekte einer Klasse werden im Heapspeicher erstellt
Die Klasse wird zur Wiederverwendbarkeit verwendet, während die Struktur zum Gruppieren der Daten in derselben Struktur verwendet wird
Strukturdatenelemente können nicht direkt initialisiert werden, sondern können von außerhalb der Struktur zugewiesen werden
Klassendatenelemente können direkt vom Konstruktor ohne Parameter initialisiert und vom parametrisierten Konstruktor zugewiesen werden
quelle