risposta-alla-domanda-sullo-sviluppo-web-bd.com

Perché scegliere Struct Over Class?

Giocando con Swift, proveniente da uno sfondo Java, perché dovresti scegliere una Struct invece di una Classe? Sembra che siano la stessa cosa, con una Struct che offre meno funzionalità. Perché sceglierlo allora?

423
bluedevil2k

Secondo il popolare protocollo WWDC 2015 Talk Oriented Programming in Swift ( video , transcript ), Swift offre una serie di funzionalità che rendono le strutture migliori delle classi in molte circostanze.

Le strutture sono preferibili se sono relativamente piccole e copiabili perché copiare è molto più sicuro che avere più riferimenti alla stessa istanza che accade con le classi. Ciò è particolarmente importante quando si passa da una variabile a molte classi e/o in un ambiente con multithreading. Se puoi sempre inviare una copia della tua variabile ad altri luoghi, non devi mai preoccuparti di quell'altro posto che cambia il valore della tua variabile sotto di te.

Con Structs, c'è molto meno bisogno di preoccuparsi di perdite di memoria o di più thread racing per accedere/modificare una singola istanza di una variabile. (Per chi ha una mentalità più tecnica, l'eccezione è quando si cattura una struttura all'interno di una chiusura perché in realtà sta acquisendo un riferimento all'istanza a meno che non lo si contrassegni esplicitamente per essere copiato).

Le classi possono anche diventare gonfie perché una classe può ereditare solo da una singola superclasse. Questo ci incoraggia a creare enormi superclassi che racchiudono molte abilità diverse che sono solo vagamente correlate. L'utilizzo di protocolli, in particolare con estensioni di protocollo in cui è possibile fornire implementazioni ai protocolli, consente di eliminare la necessità per le classi di conseguire questo tipo di comportamento.

Il discorso espone questi scenari in cui le classi sono preferite:

  • Copiare o confrontare le istanze non ha senso (ad es. Finestra)
  • La durata dell'istanza è legata ad effetti esterni (ad esempio, TemporaryFile)
  • Le istanze sono solo "sink" - conduttori di sola scrittura allo stato esterno (ad es ..CGContext)

Implica che le strutture dovrebbero essere l'impostazione predefinita e le classi dovrebbero essere un fallback.

D'altra parte, The Swift Programming Language documentation è in qualche modo contraddittorio:

Le istanze della struttura vengono sempre passate per valore e class le istanze vengono sempre passate per riferimento. Ciò significa che sono adatto a diversi tipi di compiti. Considerando i dati costrutti e funzionalità necessari per un progetto, decidere se ogni costrutto di dati debba essere definito come una classe o come struttura.

Come linea guida generale, considera la possibilità di creare una struttura quando uno o più di queste condizioni si applicano:

  • Lo scopo principale della struttura è incapsulare alcuni valori di dati relativamente semplici.
  • È ragionevole aspettarsi che i valori incapsulati vengano copiati anziché riferiti quando si assegna o si passa un istanza di quella struttura.
  • Tutte le proprietà memorizzate dalla struttura sono esse stesse dei tipi di valore, che dovrebbero anche essere copiati anziché riferiti.
  • La struttura non ha bisogno di ereditare proprietà o comportamenti da un altro tipo esistente.

Esempi di buoni candidati per le strutture includono:

  • La dimensione di una forma geometrica, forse incapsulando una proprietà width e una proprietà height, entrambe di tipo Double.
  • Un modo per fare riferimento a intervalli all'interno di una serie, forse incapsulare una proprietà di avvio e una proprietà di lunghezza, entrambi di tipo Int.
  • Un punto in un sistema di coordinate 3D, forse incapsulando le proprietà x, yez, ciascuna di tipo Double.

In tutti gli altri casi, definire una classe e creare istanze di tale classe essere gestito e passato per riferimento. In pratica, questo significa che la maggior parte dei costrutti di dati personalizzati dovrebbero essere classi, non strutture.

Qui si afferma che dovremmo utilizzare di default le classi e utilizzare le strutture solo in circostanze specifiche. In definitiva, è necessario comprendere l'implicazione del mondo reale dei tipi di valore rispetto ai tipi di riferimento e quindi è possibile prendere una decisione informata su quando utilizzare le strutture o le classi. Inoltre, tieni presente che questi concetti sono in continua evoluzione e la documentazione di Swift Programming Language è stata scritta prima che fosse data la presentazione della Programm Oriented Programming.

499
drewag

Poiché le istanze struct sono allocate nello stack e le istanze di classe sono allocate nell'heap, le struct possono essere drasticamente più veloci.

Tuttavia, dovresti sempre misurarlo tu stesso e decidere in base al tuo caso d'uso unico.

Considera il seguente esempio, che dimostra 2 strategie di avvolgimento del tipo di dati Int usando struct e class. Sto usando 10 valori ripetuti per riflettere meglio il mondo reale, dove hai più campi.

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)
}

Le prestazioni sono misurate usando

// 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)")
}

Il codice può essere trovato all'indirizzo https://github.com/knguyen2708/StructVsClassPerformance

UPDATE (27 marzo 2018):

A partire da Swift 4.0, Xcode 9.2, con versione di rilascio su iPhone 6S, iOS 11.2.6, l'impostazione di Swift Compiler è -O -whole-module-optimization:

  • La versione class ha impiegato 2,06 secondi
  • La versione struct ha impiegato 4.17e-08 secondi (50.000.000 di volte più velocemente)

(Non ho più mediamente più esecuzioni, poiché le varianze sono molto piccole, meno del 5%)

Note: la differenza è molto meno drammatica senza l'ottimizzazione dell'intero modulo. Sarei felice se qualcuno potesse indicare cosa fa effettivamente la bandiera.


UPDATE (7 maggio 2016):

A partire da Swift 2.2.1, Xcode 7.3, con versione di rilascio su iPhone 6S, iOS 9.3.1, con una media di 5 esecuzioni, l'impostazione di Swift Compiler è -O -whole-module-optimization:

  • La versione class ha preso 2.159942142s
  • La versione struct ha preso 5.83E-08s (37.000.000 di volte più veloce)

Nota: come qualcuno ha detto che negli scenari del mondo reale, ci sarà probabilmente più di 1 campo in una struttura, ho aggiunto test per structs/classes con 10 campi anziché 1. Sorprendentemente, i risultati non variano tanto.


RISULTATI ORIGINALI (1 giugno 2014):

(Eseguito su struct/class con 1 campo, non 10)

A partire da Swift 1.2, Xcode 6.3.2, l'esecuzione di Release build su iPhone 5S, iOS 8.3, ha una durata media di 5 esecuzioni

  • La versione class ha preso 9.788332333s
  • La versione struct ha richiesto 0,010532942 (900 volte più veloce)

VECCHI RISULTATI (dal momento sconosciuto)

(Eseguito su struct/class con 1 campo, non 10)

Con la versione build sul mio MacBook Pro:

  • La versione class ha impiegato 1.10082 sec
  • La versione struct ha impiegato 0.02324 sec (50 volte più veloce)
148
Khanh Nguyen

Somiglianze tra strutture e classi.

Ho creato Gist per questo con semplici esempi . https://github.com/objc-Swift/swift-classes-vs-structures

E differenze

1. Eredità.

le strutture non possono ereditare in Swift. Se vuoi

class Vehicle{
}

class Car : Vehicle{
}

Vai per un corso.

2. Passa

Le strutture Swift passano per valore e le istanze di classe passano per riferimento.

Differenze contestuali

Struct costante e variabili

Esempio (utilizzato a WWDC 2014)

struct Point{

   var x = 0.0;
   var y = 0.0;

} 

Definisce una struttura chiamata Punto.

var point = Point(x:0.0,y:2.0)

Ora se provo a cambiare la x. È un'espressione valida.

point.x = 5

Ma se definissi un punto come costante.

let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.

In questo caso l'intero punto è costante immutabile. 

Se ho usato un punto di classe invece questa è un'espressione valida. Perché in una costante immutabile di classe è il riferimento alla classe stessa non alle sue variabili di istanza (A meno che quelle variabili siano definite come costanti)

59
MadNik

Ecco alcuni altri motivi da considerare:

  1. le strutture ottengono un inizializzatore automatico che non è necessario mantenere nel codice.

    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")
    

Per ottenere questo in una classe, dovresti aggiungere l'inizializzatore e mantenere l'intializer ... 

  1. I tipi di raccolta di base come Array sono le strutture. Più li usi nel tuo codice, più ti abituerai a passare per valore anziché come riferimento. Per esempio:

    func removeLast(var array:[String]) {
       array.removeLast()
       println(array) // [one, two]
    }
    
    var someArray = ["one", "two", "three"]
    removeLast(someArray)
    println(someArray) // [one, two, three]
    
  2. Apparentemente immutabilità vs. mutabilità è un argomento enorme, ma molte persone intelligenti pensano che l'immutabilità - le strutture in questo caso - siano preferibili. Oggetti mutabili e immutabili

28
Dan Rosenstark

Supponendo che conosciamo Struct è un tipo valore e Classe è un tipo di riferimento.

Se non sai che tipo di valore e tipo di riferimento vedi vedi Qual è la differenza tra il passaggio per riferimento e il passaggio per valore?

Basato su il post di mikeash :

... Vediamo prima alcuni esempi estremi e ovvi. Gli integer sono ovviamente copiabile. Dovrebbero essere tipi di valore. I socket di rete non possono essere copiato in modo sensato Dovrebbero essere tipi di riferimento. Punti, come in x, y coppie, sono copiabili. Dovrebbero essere tipi di valore. Un controller che rappresenta un disco non può essere copiato in modo ragionevole. Questo dovrebbe essere un riferimento genere.

Alcuni tipi possono essere copiati ma potrebbe non essere qualcosa che vuoi capita tutto il tempo Questo suggerisce che dovrebbero essere di riferimento tipi. Ad esempio, un pulsante sullo schermo può essere copiato concettualmente . La copia non sarà del tutto identica all'originale. Un clic sul la copia non attiverà l'originale. La copia non occuperà lo stesso posizione sullo schermo. Se passi il pulsante o lo metti in un nuova variabile probabilmente vorrai fare riferimento al pulsante originale e vuoi solo fare una copia quando è richiesta esplicitamente. Quello significa che il tuo tipo di pulsante dovrebbe essere un tipo di riferimento.

I controller di vista e finestra sono un esempio simile. Potrebbero essere riproducibile, presumibilmente, ma non è quasi mai quello che vorresti fare . Dovrebbero essere tipi di riferimento.

E i tipi di modelli? Potresti avere un tipo di utente che rappresenta un utente sul tuo sistema, o un tipo di crimine che rappresenta un'azione presa da un Utente. Questi sono piuttosto copiabili, quindi dovrebbero probabilmente essere value tipi. Tuttavia, probabilmente desideri aggiornamenti a Crime utente creati in un posto nel tuo programma per essere visibile ad altre parti del programma . Questo suggerisce che i tuoi utenti dovrebbero essere gestiti da qualche tipo di utente controller che sarebbe un tipo di riferimento. per esempio

struct User {}
class UserController {
    var users: [User]

    func add(user: User) { ... }
    func remove(userNamed: String) { ... }
    func ...
}

Le collezioni sono un caso interessante. Questi includono cose come gli array e dizionari, così come stringhe. Sono copiabili? Ovviamente. È copiare qualcosa che vuoi accadere facilmente e spesso? Questo è meno chiaro.

La maggior parte delle lingue dice "no" a questo e rende le loro collezioni di riferimento tipi. Questo è vero in Objective-C e Java, Python e JavaScript e quasi tutte le altre lingue a cui riesco a pensare. (Un'eccezione importante È C++ con tipi di raccolta STL, ma C++ è il pazzo delirante del mondo linguistico Che fa tutto stranamente.)

Swift ha detto "sì", il che significa che tipi come Array e Dictionary e Le stringhe sono strutture piuttosto che classi. Vengono copiati sul compito, e passandoli come parametri. Questa è una scelta del tutto ragionevole fino a quando la copia è a buon mercato, che Swift prova molto a realizzare . ...

Inoltre, non utilizzare la classe quando si deve eseguire l'override di ogni singola istanza di una funzione, vale a dire che non hanno alcuna condivisa funzionalità.

Quindi, invece di avere diverse sottoclassi di una classe. Utilizzare diverse strutture conformi a un protocollo.

18
Honey

Alcuni vantaggi:

  • automaticamente thread-safe per non essere condivisibile
  • usa meno memoria a causa di no isa e refcount (e in effetti è lo stack allocato in genere)
  • i metodi vengono sempre inviati staticamente, quindi possono essere sottolineati (anche se @final può farlo per le classi)
  • più facile da ragionare (non c'è bisogno di "copiare in modo difensivo" come è tipico con NSArray, NSString, ecc ...) per lo stesso motivo della sicurezza del thread
18
Catfish_Man

La struttura è molto più veloce di Class. Inoltre, se hai bisogno di ereditarietà, devi usare Class. Il punto più importante è che la classe è un tipo di riferimento mentre la struttura è un tipo di valore. per esempio,

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  
}

ora consente di creare l'istanza di entrambi.

var flightA = Flight()

var flightB = Flight2.init(id: 100, description:"first ever flight of Virgin Airlines", destination:"london" , airlines:"Virgin Airlines" )

ora passiamo queste istanze a due funzioni che modificano l'id, la descrizione, la destinazione ecc.

func modifyFlight(flight:Flight) -> Void {
    flight.id = 200
    flight.description = "second flight of Virgin Airlines"
    flight.destination = "new york"
    flight.airlines = "Virgin Airlines"
}

anche,

func modifyFlight2(flight2: Flight2) -> Void {
    var passedFlight = flight2
    passedFlight.id = 200
    passedFlight.description = "second flight from virgin airlines" 
}

così,

modifyFlight(flight: flightA)
modifyFlight2(flight2: flightB)

ora se stampiamo l'ID e la descrizione del flightA, otteniamo

id = 200
description = "second flight of Virgin Airlines"

Qui, possiamo vedere l'id e la descrizione di FlightA è cambiato perché il parametro passato al metodo modify punta effettivamente all'indirizzo di memoria dell'oggetto flightA (tipo di riferimento).

ora se stampiamo l'id e la descrizione dell'istanza FLightB otteniamo, 

id = 100
description = "first ever flight of Virgin Airlines"

Qui possiamo vedere che l'istanza FlightB non viene modificata perché nel metodo modifyFlight2, l'istanza effettiva di Flight2 è passata anziché di riferimento (tipo di valore).

12
Manoj Karki

Rispondendo alla domanda dal punto di vista dei tipi di valore e dei tipi di riferimento, da questo post sul blog di Apple sembrerebbe molto semplice:

Utilizza un tipo di valore [ad es. struct, enum] quando:

  • Confrontare i dati di istanza con == ha senso
  • Vuoi che le copie abbiano uno stato indipendente
  • I dati verranno utilizzati nel codice attraverso più thread

Utilizza un tipo di riferimento [ad es. classe] quando:

  • Confrontare l'identità dell'istanza con === ha senso
  • Vuoi creare uno stato condiviso e mutevole

Come accennato in questo articolo, una classe senza proprietà scrivibili si comporterà identicamente con una struct, con (aggiungerò) un avvertimento: le strutture sono le migliori per i modelli thread-safe - un requisito sempre più imminente nella moderna architettura delle app .

4
David James

Con le classi si ottiene l'ereditarietà e si passa per riferimento, le strutture non hanno ereditarietà e vengono passate per valore.

Ci sono grandi sessioni WWDC su Swift, a questa domanda specifica viene data una risposta dettagliata in uno di essi. Assicurati di guardarli, in quanto ti consentirà di accelerare molto più velocemente rispetto alla guida della lingua o all'iBook.

3
Joride

Non direi che le strutture offrono meno funzionalità.

Certo, il sé è immutabile tranne che in una funzione mutante, ma questo è tutto.

L'ereditarietà funziona bene finché si mantiene la buona idea che ogni classe dovrebbe essere astratta o definitiva.

Implementa classi astratte come protocolli e classi finali come strutture.

La cosa bella delle structs è che puoi rendere i tuoi campi mutabili senza creare uno stato mutabile condiviso perché la copia su write si occupa di questo :)

Ecco perché le proprietà/campi nel seguente esempio sono tutti mutabili, cosa che non farei in Java o C # o Swift classes .

Esempio di struttura di ereditarietà con un po 'di uso sporco e diretto in basso nella funzione denominata "esempio":

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)")
}
2
yeoman

In Swift, è stato introdotto un nuovo modello di programmazione noto come programmazione orientata al protocollo.

Modello creativo:

In Swift, Struct è un valore tipi che vengono automaticamente clonati. Pertanto otteniamo il comportamento richiesto per implementare il modello prototipo gratuitamente. 

Mentre classes sono il tipo di riferimento, che non viene automaticamente clonato durante l'assegnazione. Per implementare il modello prototipo, le classi devono adottare il protocollo NSCopying.


La copia poco profonda duplica solo il riferimento, che punta a quegli oggetti mentre copia profonda duplica il riferimento dell'oggetto.


Implementare deep copy per ogni tipo di riferimento è diventato un compito noioso. Se le classi includono un ulteriore tipo di riferimento, dobbiamo implementare un modello prototipo per ciascuna delle proprietà dei riferimenti. E quindi dobbiamo effettivamente copiare l'intero grafico degli oggetti implementando il protocollo NSCopying.

class Contact{
  var firstName:String
  var lastName:String
  var workAddress:Address // Reference type
}

class Address{
   var street:String
   ...
} 

Usando structs ed enums, abbiamo reso il nostro codice più semplice dal momento che non dobbiamo implementare la logica di copia.

2
Balasubramanian

Structs sono value type e Classes sono reference type

  • I tipi di valore sono più veloci dei tipi di riferimento
  • Le istanze del tipo valore sono sicure in un ambiente a più thread poiché Più thread possono mutare l'istanza senza doversi preoccupare Delle condizioni della competizione o dei deadlock
  • Il tipo di valore non ha riferimenti diversi dal tipo di riferimento; quindi non c'è nessuna perdita di memoria.

Usa un tipo value quando:

  • Se si desidera che le copie abbiano uno stato indipendente, i dati verranno utilizzati nel codice Su più thread

Usa un tipo reference quando:

  • Vuoi creare uno stato condiviso e mutevole.

Ulteriori informazioni possono essere trovate anche nella documentazione di Apple

https://docs.Swift.org/Swift-book/LanguageGuide/ClassesAndStructures.html


Informazioni aggiuntive

I tipi di valore Swift vengono mantenuti nello stack. In un processo, ogni thread ha il proprio spazio di stack, quindi nessun altro thread sarà in grado di accedere direttamente al tipo di valore. Quindi nessuna condizione di gara, blocco, deadlock o qualsiasi complessità di sincronizzazione del thread correlata.

I tipi di valore non richiedono l'allocazione dinamica della memoria o il conteggio dei riferimenti, che sono entrambi operazioni costose. Allo stesso tempo i metodi sui tipi di valore vengono inviati staticamente. Questi creano un enorme vantaggio a favore dei tipi di valore in termini di prestazioni.

Come promemoria ecco un elenco di Swift 

Tipi di valore:

  • Struct
  • Enum
  • Tuple
  • Primitivi (Int, Double, Bool ecc.)
  • Collezioni (matrice, stringa, dizionario, set)

Tipi di riferimento:

  • Classe
  • Qualunque cosa provenga da NSObject
  • Funzione
  • Chiusura
1
casillas

Molte API Cocoa richiedono sottoclassi NSObject, che ti obbligano a utilizzare la classe. Ma a parte questo, puoi utilizzare i seguenti casi del blog Swift di Apple per decidere se utilizzare un tipo di valore struct/enum o un tipo di riferimento di classe.

https://developer.Apple.com/Swift/blog/?id=10

1
akshay

Poiché struct sono tipi di valore e puoi creare facilmente la memoria che memorizza nello stack.Struct può essere facilmente accessibile e dopo l'ambito del lavoro è facilmente deallocato dalla memoria dello stack tramite pop dall'alto dello stack On l'altra classe è un tipo di riferimento che memorizza nell'heap e le modifiche apportate in un oggetto di classe avranno un impatto su un altro oggetto poiché sono strettamente accoppiate e il tipo di riferimento. Tutti i membri di una struttura sono pubblici mentre tutti i membri di una classe sono privati.

Gli svantaggi di struct è che non può essere ereditato.

0
Tapash Mollick

Un punto che non ottiene attenzione in queste risposte è che una variabile che tiene una classe contro una struct può essere un let pur consentendo modifiche sulle proprietà dell'oggetto, mentre non è possibile farlo con una struct.

Ciò è utile se non si desidera che la variabile punti mai a un altro oggetto, ma è comunque necessario modificare l'oggetto, ad esempio nel caso di molte variabili di istanza che si desidera aggiornare una dopo l'altra. Se si tratta di una struttura, è necessario consentire la reimpostazione della variabile su un altro oggetto utilizzando var per fare ciò, poiché un tipo di valore costante in Swift consente correttamente la mutazione zero, mentre i tipi di riferimento (classi) non si comportano in questo modo .

0
johnbakers