Bawąc się Swiftem, wywodzącym się ze środowiska Java, dlaczego miałbyś wybrać Strukturę zamiast klasy? Wygląda na to, że są tym samym, a Struct oferuje mniej funkcjonalności. Dlaczego więc to wybrać?
Bawąc się Swiftem, wywodzącym się ze środowiska Java, dlaczego miałbyś wybrać Strukturę zamiast klasy? Wygląda na to, że są tym samym, a Struct oferuje mniej funkcjonalności. Dlaczego więc to wybrać?
Odpowiedzi:
Zgodnie z bardzo popularnym przemówieniem WWDC 2015 Programowanie zorientowane na protokół w Swift ( wideo , transkrypcja ), Swift oferuje szereg funkcji, które w wielu okolicznościach sprawiają, że struktury są lepsze niż klasy.
Struktury są preferowane, jeśli są stosunkowo małe i można je kopiować, ponieważ kopiowanie jest znacznie bezpieczniejsze niż posiadanie wielu odwołań do tego samego wystąpienia, co dzieje się w przypadku klas. Jest to szczególnie ważne przy przekazywaniu zmiennej do wielu klas i / lub w środowisku wielowątkowym. Jeśli zawsze możesz wysłać kopię swojej zmiennej do innych miejsc, nigdy nie musisz się martwić, że to inne miejsce zmieni wartość twojej zmiennej pod tobą.
Dzięki Strukturom nie musisz martwić się o wycieki pamięci lub ściganie się wielu wątków, aby uzyskać dostęp / modyfikować pojedyncze wystąpienie zmiennej. (Dla bardziej technicznie myślących wyjątkiem jest to podczas przechwytywania struktury wewnątrz zamknięcia, ponieważ wtedy faktycznie przechwytuje odwołanie do instancji, chyba że wyraźnie zaznaczysz ją do skopiowania).
Klasy mogą również ulec rozdęciu, ponieważ klasa może dziedziczyć tylko z jednej nadklasy. To zachęca nas do tworzenia ogromnych superklas obejmujących wiele różnych umiejętności, które są jedynie luźno powiązane. Korzystanie z protokołów, zwłaszcza z rozszerzeniami protokołów, w których można zapewnić implementacje protokołów, pozwala wyeliminować potrzebę klas, aby osiągnąć tego rodzaju zachowanie.
W wykładzie przedstawiono te scenariusze, w których preferowane są klasy:
- Kopiowanie lub porównywanie instancji nie ma sensu (np. Window)
- Czas życia instancji jest powiązany z efektami zewnętrznymi (np. Plik tymczasowy)
- Instancje to tylko „ujścia” - przewody tylko do zapisu do stanu zewnętrznego (np.CGContext)
Oznacza to, że struktury powinny być domyślnymi, a klasy powinny być rezerwowe.
Z drugiej strony dokumentacja Swift Programming Language jest nieco sprzeczna:
Instancje struktury są zawsze przekazywane przez wartość, a instancje klasy są zawsze przekazywane przez referencję. Oznacza to, że nadają się do różnego rodzaju zadań. Rozważając konstrukcje danych i funkcje potrzebne do projektu, zdecyduj, czy każda konstrukcja danych powinna być zdefiniowana jako klasa czy struktura.
Jako ogólną wskazówkę rozważ utworzenie struktury, gdy spełniony zostanie jeden lub więcej z tych warunków:
- Podstawowym celem struktury jest zamknięcie kilku stosunkowo prostych wartości danych.
- Rozsądne jest oczekiwanie, że enkapsulowane wartości zostaną skopiowane, a nie odwołane, gdy przypiszesz lub przekażesz instancję tej struktury.
- Wszelkie właściwości przechowywane przez strukturę same w sobie są typami wartości, które również powinny zostać skopiowane, a nie odwołane.
- Struktura nie musi dziedziczyć właściwości ani zachowania z innego istniejącego typu.
Przykłady dobrych kandydatów do struktur obejmują:
- Rozmiar kształtu geometrycznego, być może zawierającego właściwość width i height, oba typu Double.
- Sposób odwoływania się do zakresów w szeregu, być może zawierających właściwość początkową i właściwość długości, oba typu Int.
- Punkt w układzie współrzędnych 3D, być może zawierający właściwości x, y i z, każdy typu Double.
We wszystkich innych przypadkach zdefiniuj klasę i utwórz instancje tej klasy, które będą zarządzane i przekazywane przez referencję. W praktyce oznacza to, że większość niestandardowych konstrukcji danych powinna być klasami, a nie strukturami.
Tutaj twierdzi, że powinniśmy domyślnie używać klas i używać struktur tylko w określonych okolicznościach. Ostatecznie musisz zrozumieć rzeczywiste implikacje typów wartości w porównaniu z typami referencyjnymi, a następnie możesz podjąć świadomą decyzję, kiedy użyć struktur lub klas. Pamiętaj też, że koncepcje te ciągle ewoluują, a dokumentacja The Swift Programming Language została napisana przed przemówieniem na temat programowania zorientowanego na protokół.
In practice, this means that most custom data constructs should be classes, not structures.
Czy możesz mi wyjaśnić, jak po przeczytaniu tego dostajesz, że większość zbiorów danych powinna być strukturami, a nie klasami? Dali określony zestaw reguł, kiedy coś powinno być strukturą, i prawie powiedzieli „wszystkie inne scenariusze, w których klasa jest lepsza”.
Ponieważ instancje struktur są przydzielane na stosie, a instancje klas są przydzielane na stercie, struktury mogą czasami być znacznie szybsze.
Jednak zawsze powinieneś zmierzyć to sam i zdecydować na podstawie unikalnego przypadku użycia.
Rozważ następujący przykład, który pokazuje 2 strategie zawijania Int
typu danych za pomocą struct
i class
. Używam 10 powtarzających się wartości, aby lepiej odzwierciedlić rzeczywisty świat, w którym masz wiele pól.
class Int10Class {
let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
init(_ val: Int) {
self.value1 = val
self.value2 = val
self.value3 = val
self.value4 = val
self.value5 = val
self.value6 = val
self.value7 = val
self.value8 = val
self.value9 = val
self.value10 = val
}
}
struct Int10Struct {
let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
init(_ val: Int) {
self.value1 = val
self.value2 = val
self.value3 = val
self.value4 = val
self.value5 = val
self.value6 = val
self.value7 = val
self.value8 = val
self.value9 = val
self.value10 = val
}
}
func + (x: Int10Class, y: Int10Class) -> Int10Class {
return IntClass(x.value + y.value)
}
func + (x: Int10Struct, y: Int10Struct) -> Int10Struct {
return IntStruct(x.value + y.value)
}
Wydajność jest mierzona za pomocą
// Measure Int10Class
measure("class (10 fields)") {
var x = Int10Class(0)
for _ in 1...10000000 {
x = x + Int10Class(1)
}
}
// Measure Int10Struct
measure("struct (10 fields)") {
var y = Int10Struct(0)
for _ in 1...10000000 {
y = y + Int10Struct(1)
}
}
func measure(name: String, @noescape block: () -> ()) {
let t0 = CACurrentMediaTime()
block()
let dt = CACurrentMediaTime() - t0
print("\(name) -> \(dt)")
}
Kod można znaleźć na https://github.com/knguyen2708/StructVsClassPerformance
AKTUALIZACJA (27 marca 2018 r.) :
Począwszy od Swift 4.0, Xcode 9.2, z uruchomioną wersją Release na iPhone 6S, iOS 11.2.6, ustawienia kompilatora Swift to -O -whole-module-optimization
:
class
wersja zajęła 2.06 sekundstruct
wersja zajęła 4.17e-08 sekund (50 000 000 razy szybciej)(Już nie uśredniam wielu przebiegów, ponieważ wariancje są bardzo małe, poniżej 5%)
Uwaga : różnica jest znacznie mniej dramatyczna bez optymalizacji całego modułu. Byłbym zadowolony, gdyby ktoś mógł wskazać, co faktycznie robi flaga.
AKTUALIZACJA (7 maja 2016 r.) :
Począwszy od Swift 2.2.1, Xcode 7.3, z uruchomioną wersją Release na iPhone 6S, iOS 9.3.1, uśredniony dla 5 uruchomień, ustawienie kompilatora Swift to -O -whole-module-optimization
:
class
wersja zajęła 2.159942142sstruct
wersja zajęła 5.83E-08s (37.000.000 razy szybciej)Uwaga : jak ktoś wspomniał, że w rzeczywistych scenariuszach w strukturze będzie prawdopodobnie więcej niż 1 pole, dodałem testy struktur / klas z 10 polami zamiast 1. Zaskakujące, wyniki nie różnią się zbytnio.
ORYGINALNE WYNIKI (1 czerwca 2014 r.):
(Ran na struct / class z 1 polem, a nie 10)
Począwszy od wersji Swift 1.2, Xcode 6.3.2, z uruchomioną wersją Release na iPhone'a 5S, iOS 8.3, uśredniono dla 5 przebiegów
class
wersja zajęła 9,788332333sstruct
wersja zajęła 0,010532942s (900 razy szybciej)STARE WYNIKI (z nieznanego czasu)
(Ran na struct / class z 1 polem, a nie 10)
W wersji kompilacji na moim MacBooku Pro:
class
Wersja trwało 1.10082 sekstruct
Wersja trwało 0.02324 sekund (50 razy szybciej)Stworzyłem do tego istotę za pomocą prostych przykładów. https://github.com/objc-swift/swift-classes-vs-structures
struktury nie mogą dziedziczyć w trybie szybkim. Jeśli chcesz
class Vehicle{
}
class Car : Vehicle{
}
Idź na zajęcia.
Struktury szybkie przechodzą według wartości, a instancje klasy są przekazywane przez odwołanie.
Stała konstrukcyjna i zmienne
Przykład (używany podczas WWDC 2014)
struct Point{
var x = 0.0;
var y = 0.0;
}
Definiuje strukturę o nazwie Punkt.
var point = Point(x:0.0,y:2.0)
Teraz, jeśli spróbuję zmienić x. To prawidłowe wyrażenie.
point.x = 5
Ale gdybym zdefiniował punkt jako stały.
let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.
W tym przypadku cały punkt jest niezmienną stałą.
Jeśli zamiast tego użyłem klasy Point, jest to prawidłowe wyrażenie. Ponieważ w niezmiennej stałej klasy jest odwołanie do samej klasy, a nie do jej zmiennych instancji (chyba że zmienne zdefiniowane jako stałe)
Oto kilka innych powodów do rozważenia:
Struktury otrzymują automatyczny inicjator, którego wcale nie musisz utrzymywać w kodzie.
struct MorphProperty {
var type : MorphPropertyValueType
var key : String
var value : AnyObject
enum MorphPropertyValueType {
case String, Int, Double
}
}
var m = MorphProperty(type: .Int, key: "what", value: "blah")
Aby uzyskać to w klasie, musisz dodać inicjator i utrzymać intializator ...
Podstawowe typy kolekcji, takie jak Array
struktury. Im częściej używasz ich we własnym kodzie, tym bardziej przyzwyczaisz się do przekazywania wartości zamiast do referencji. Na przykład:
func removeLast(var array:[String]) {
array.removeLast()
println(array) // [one, two]
}
var someArray = ["one", "two", "three"]
removeLast(someArray)
println(someArray) // [one, two, three]
Najwyraźniej niezmienność vs. zmienność to ogromny temat, ale wielu inteligentnych ludzi uważa, że niezmienność - w tym przypadku struktury - jest lepsza. Obiekty zmienne a niezmienne
internal
zakresem.
mutating
, abyś wyraźnie wiedział, jakie funkcje zmieniają ich stan. Ale ich natura jako typów wartości jest ważna. Jeśli zadeklarujesz strukturę przy pomocy let
, nie będziesz mógł wywoływać na niej żadnych funkcji mutujących. Wideo WWDC 15 na temat lepszego programowania dzięki typom wartości jest doskonałym źródłem informacji na ten temat.
Zakładając, że wiemy, że Struct jest typem wartości, a Class jest typem referencyjnym .
Jeśli nie wiesz, jaki jest typ wartości i typ odwołania, zobacz Jaka jest różnica między przekazywaniem przez referencję a przekazywaniem przez wartość?
Na podstawie postu mikeash :
... Najpierw przyjrzyjmy się ekstremalnym, oczywistym przykładom. Liczby całkowite są oczywiście możliwe do skopiowania. Powinny być typami wartości. Nie można rozsądnie skopiować gniazd sieciowych. Powinny to być typy referencyjne. Punkty, jak w parach x, y, można kopiować. Powinny być typami wartości. Kontrolera reprezentującego dysk nie można rozsądnie skopiować. To powinien być typ odniesienia.
Niektóre typy można kopiować, ale nie zawsze może to być coś, co chcesz zrobić. Sugeruje to, że powinny to być typy referencyjne. Na przykład przycisk na ekranie można koncepcyjnie skopiować. Kopia nie będzie identyczna z oryginałem. Kliknięcie kopii nie aktywuje oryginału. Kopia nie zajmie tej samej lokalizacji na ekranie. Jeśli przekażesz przycisk lub umieścisz go w nowej zmiennej, prawdopodobnie będziesz chciał odwołać się do oryginalnego przycisku i będziesz chciał wykonać kopię tylko wtedy, gdy zostanie to wyraźnie wymagane. Oznacza to, że typ przycisku powinien być typem referencyjnym.
Kontrolery widoku i okna są podobnym przykładem. Możliwe, że można je skopiować, ale prawie nigdy nie chcesz tego robić. Powinny to być typy referencyjne.
Co z typami modeli? Możesz mieć typ użytkownika reprezentujący użytkownika w twoim systemie lub typ przestępstwa reprezentujący działanie podjęte przez użytkownika. Można je kopiować, więc prawdopodobnie powinny to być typy wartości. Prawdopodobnie jednak chcesz, aby aktualizacje przestępstwa użytkownika dokonane w jednym miejscu w programie były widoczne dla innych części programu. Sugeruje to, że Twoimi użytkownikami powinien zarządzać jakiś kontroler użytkowników, który byłby typem odniesienia . na przykład
struct User {} class UserController { var users: [User] func add(user: User) { ... } func remove(userNamed: String) { ... } func ... }
Kolekcje są ciekawym przypadkiem. Należą do nich między innymi tablice i słowniki oraz ciągi znaków. Czy można je kopiować? Oczywiście. Czy kopiowanie czegoś, co chcesz zrobić, łatwo i często? To mniej jasne.
Większość języków mówi na to „nie” i podaje swoje typy referencyjne kolekcji. Dzieje się tak w Objective-C i Java, Python i JavaScript oraz prawie w każdym innym języku, jaki mogę wymyślić. (Jednym z głównych wyjątków jest C ++ z typami kolekcji STL, ale C ++ jest szalonym szaleńcem świata języków, który robi wszystko dziwnie.)
Swift powiedział „tak”, co oznacza, że typy takie jak Array i Dictionary i String są strukturami, a nie klasami. Są one kopiowane po przypisaniu i przekazaniu ich jako parametrów. Jest to całkowicie rozsądny wybór, o ile kopia jest tania, co Swift bardzo stara się osiągnąć. ...
Osobiście nie nazywam tak swoich zajęć. Zazwyczaj nazywam mój UserManager zamiast UserController, ale pomysł jest taki sam
Ponadto nie używaj klasy, gdy musisz zastąpić każdą instancję funkcji, tzn. Nie mają one żadnej wspólnej funkcji.
Zamiast mieć kilka podklas klasy. Użyj kilku struktur zgodnych z protokołem.
Innym uzasadnionym przypadkiem struktur jest sytuacja, gdy chcesz zrobić różnicę / różnicę starego i nowego modelu. W przypadku typów referencji nie można tego zrobić od razu po wyjęciu z pudełka. W przypadku typów wartości mutacje nie są współużytkowane.
Niektóre zalety:
Struktura jest znacznie szybsza niż klasa. Ponadto, jeśli potrzebujesz dziedziczenia, musisz użyć klasy. Najważniejsze jest to, że klasa jest typem odniesienia, natomiast struktura jest typem wartości. na przykład,
class Flight {
var id:Int?
var description:String?
var destination:String?
var airlines:String?
init(){
id = 100
description = "first ever flight of Virgin Airlines"
destination = "london"
airlines = "Virgin Airlines"
}
}
struct Flight2 {
var id:Int
var description:String
var destination:String
var airlines:String
}
teraz pozwala stworzyć instancję obu.
var flightA = Flight()
var flightB = Flight2.init(id: 100, description:"first ever flight of Virgin Airlines", destination:"london" , airlines:"Virgin Airlines" )
teraz pozwala przekazać te wystąpienia do dwóch funkcji, które modyfikują identyfikator, opis, miejsce docelowe itp.
func modifyFlight(flight:Flight) -> Void {
flight.id = 200
flight.description = "second flight of Virgin Airlines"
flight.destination = "new york"
flight.airlines = "Virgin Airlines"
}
również,
func modifyFlight2(flight2: Flight2) -> Void {
var passedFlight = flight2
passedFlight.id = 200
passedFlight.description = "second flight from virgin airlines"
}
więc,
modifyFlight(flight: flightA)
modifyFlight2(flight2: flightB)
teraz, jeśli wydrukujemy identyfikator i opis lotu, otrzymamy
id = 200
description = "second flight of Virgin Airlines"
Tutaj możemy zobaczyć identyfikator i opis FlightA został zmieniony, ponieważ parametr przekazany do metody modyfikacji faktycznie wskazuje adres pamięci obiektu flightA (typ odniesienia).
teraz, jeśli wydrukujemy identyfikator i opis instancji FLightB, otrzymamy,
id = 100
description = "first ever flight of Virgin Airlines"
Tutaj widzimy, że instancja FlightB nie ulega zmianie, ponieważ w metodzie editFlight2 rzeczywista instancja Flight2 jest przekazywana, a nie referencyjna (typ wartości).
Here we can see that the FlightB instance is not changed
Structs
są value type
i Classes
sąreference type
Użyj value
typu, gdy:
Użyj reference
typu, gdy:
Więcej informacji można również znaleźć w dokumentacji Apple
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
Dodatkowe informacje
Szybkie typy wartości są przechowywane na stosie. W procesie każdy wątek ma swoją własną przestrzeń stosu, więc żaden inny wątek nie będzie miał bezpośredniego dostępu do typu wartości. Stąd brak warunków wyścigu, blokad, zakleszczeń lub powiązanej złożoności synchronizacji wątków.
Typy wartości nie wymagają dynamicznego przydzielania pamięci ani liczenia referencji, które są kosztownymi operacjami. Jednocześnie metody dla typów wartości są wysyłane statycznie. Stanowią one ogromną przewagę na korzyść rodzajów wartości pod względem wydajności.
Dla przypomnienia jest tu lista Swift
Typy wartości:
Typy referencyjne:
Odpowiadając na pytanie z perspektywy typów wartości a typów referencyjnych, z tego postu na blogu Apple byłoby to bardzo proste:
Użyj typu wartości [np. Struct, enum], gdy:
- Porównanie danych instancji z == ma sens
- Chcesz, aby kopie miały niezależny stan
- Dane zostaną wykorzystane w kodzie w wielu wątkach
Użyj typu odniesienia [np. Klasa], gdy:
- Porównanie tożsamości instancji z === ma sens
- Chcesz stworzyć współdzielony, zmienny stan
Jak wspomniano w tym artykule, klasa bez właściwości do zapisu będzie zachowywać się identycznie ze strukturą, z (dodam) jednym zastrzeżeniem: struktury są najlepsze dla modeli bezpiecznych dla wątków - jest to coraz większe wymaganie we współczesnej architekturze aplikacji.
W przypadku klas otrzymujesz dziedziczenie i są przekazywane przez referencje, struktury nie mają dziedziczenia i są przekazywane przez wartość.
Na platformie Swift odbywają się świetne sesje WWDC, na to szczegółowe pytanie udzielono szczegółowo w jednej z nich. Oglądaj je, ponieważ przyspieszy to znacznie szybciej niż Przewodnik językowy lub iBook.
Nie powiedziałbym, że struktury oferują mniejszą funkcjonalność.
Oczywiście, jaźń jest niezmienna, z wyjątkiem funkcji mutacji, ale o to chodzi.
Dziedziczenie działa dobrze, dopóki trzymasz się starego, dobrego pomysłu, że każda klasa powinna być abstrakcyjna lub ostateczna.
Implementuj klasy abstrakcyjne jako protokoły, a klasy końcowe jako struktury.
Zaletą struktur jest to, że możesz modyfikować swoje pola bez tworzenia współdzielonego stanu mutable, ponieważ zajmuje się tym kopiowanie przy zapisie :)
Dlatego wszystkie właściwości / pola w poniższym przykładzie są zmienne, czego nie zrobiłbym w Javie, C # lub klasach szybkich .
Przykładowa struktura dziedziczenia z nieco brudnym i prostym użyciem u dołu w funkcji o nazwie „przykład”:
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("A time event: \(event)")
}
func visit(event: StatusEvent)
{
print("A status event: \(event)")
if let change = event as? StatusChangedStatusEvent
{
status = change.newStatus
}
}
}
let visitor = Visitor()
readEvent(1).accept(visitor)
print("status: \(visitor.status)")
}
W Swift wprowadzono nowy wzorzec programowania znany jako Programowanie zorientowane na protokół.
Wzór kreacyjny:
W skrócie Struct to typy wartości które są automatycznie klonowane. Dlatego otrzymujemy wymagane zachowanie do zaimplementowania wzorca prototypu za darmo.
Natomiast klasy są typem referencyjnym, który nie jest automatycznie klonowany podczas przypisywania. Aby zaimplementować wzorzec prototypowy, klasy muszą przyjąć NSCopying
protokół.
Płytka kopia powiela tylko odniesienie, które wskazuje na te obiekty, natomiast głęboka kopia powiela odniesienie do obiektu.
Wdrożenie głębokiej kopii dla każdego typu odniesienia stało się żmudnym zadaniem. Jeśli klasy zawierają dodatkowy typ odwołania, musimy zaimplementować wzorzec prototypu dla każdej właściwości odwołania. A następnie musimy skopiować cały wykres obiektów, implementując NSCopying
protokół.
class Contact{
var firstName:String
var lastName:String
var workAddress:Address // Reference type
}
class Address{
var street:String
...
}
Używając struktur i wyliczeń , uprościliśmy nasz kod, ponieważ nie musimy implementować logiki kopiowania.
Wiele interfejsów API Cocoa wymaga podklas NSObject, co zmusza cię do korzystania z klasy. Ale poza tym możesz użyć następujących przypadków z bloga Swift firmy Apple, aby zdecydować, czy użyć typu wartości struct / enum, czy typu odwołania do klasy.
Jednym z punktów, na który nie zwraca się uwagi w tych odpowiedziach, jest to, że zmienna utrzymująca klasę względem struktury może przez jakiś let
czas nadal pozwalać na zmiany właściwości obiektu, podczas gdy nie można tego zrobić za pomocą struktury.
Jest to przydatne, jeśli nie chcesz, aby zmienna kiedykolwiek wskazywała na inny obiekt, ale nadal musisz zmodyfikować obiekt, tj. W przypadku wielu zmiennych instancji, które chcesz aktualizować jedna po drugiej. Jeśli jest to struktura, musisz w tym celu zresetować zmienną do innego obiektu var
, ponieważ stały typ wartości w Swift poprawnie pozwala na zerową mutację, podczas gdy typy referencyjne (klasy) nie zachowują się w ten sposób.
Struktury są typami wartości i można bardzo łatwo utworzyć pamięć, która przechowuje w stosie. Strukt może być łatwo dostępny, a po zakresie pracy można go łatwo zwolnić z pamięci stosu poprzez pop z góry stosu. Z drugiej strony klasa jest typem odniesienia, który przechowuje w stosie, a zmiany dokonane w obiekcie jednej klasy będą miały wpływ na inny obiekt, ponieważ są ściśle powiązane i typu odniesienia. Wszyscy członkowie struktury są publiczni, podczas gdy wszyscy członkowie klasy są prywatni .
Wadą struktury jest to, że nie można jej odziedziczyć.
Struktura i klasa są typami danych definiowanymi przez użytkownika
Domyślnie struktura jest publiczna, podczas gdy klasa jest prywatna
Klasa implementuje zasadę enkapsulacji
Obiekty klasy są tworzone w pamięci sterty
Klasa służy do ponownego wykorzystania, podczas gdy struktura służy do grupowania danych w tej samej strukturze
Elementy danych struktury nie mogą być inicjowane bezpośrednio, ale można je przypisać na zewnątrz struktury
Elementy danych klasy mogą być inicjowane bezpośrednio przez konstruktor bez parametrów i przypisywane przez sparametryzowany konstruktor