Swift passa per valore o passa per riferimento

Sono davvero nuovo di Swift e leggo solo che le classi vengono passate per riferimento e le matrici / stringhe ecc. Vengono copiate.

Il passaggio si riferisce allo stesso modo di Objective-C o Java in cui in realtà si passa il riferimento “a” o è corretto passare per riferimento?

Tipi di cose in Swift

La regola è:

  • Le istanze di class sono tipi di riferimento (ovvero il tuo riferimento a un’istanza di class è effettivamente un puntatore )

  • Le funzioni sono tipi di riferimento

  • Tutto il resto è un tipo di valore ; “Tutto il resto” significa semplicemente istanze di strutture e istanze di enumerazione, perché è tutto ciò che c’è in Swift. Array e stringhe sono istanze di struct, ad esempio. È ansible passare un riferimento a una di queste cose (come argomento di funzione) utilizzando inout e prendendo l’indirizzo, come evidenziato da newacct. Ma il tipo è di per sé un tipo di valore.

Quali tipi di riferimento significano per te

Un object di tipo riferimento è speciale nella pratica perché:

  • Il semplice assegnamento o passaggio alla funzione può produrre più riferimenti allo stesso object

  • L’object stesso è mutabile anche se il riferimento ad esso è una costante ( let , esplicita o implicita).

  • Una mutazione all’object influisce su quell’object visto da tutti i riferimenti ad esso.

Questi possono essere pericoli, quindi tieni d’occhio. D’altra parte, il passaggio di un tipo di riferimento è chiaramente efficiente perché solo un puntatore viene copiato e passato, il che è banale.

Quali tipi di valore significano per te

Chiaramente, il passaggio di un tipo di valore è “più sicuro”, e let intendere ciò che dice: non è ansible mutare un’istanza struct o un’istanza enum attraverso un riferimento let . D’altra parte, tale sicurezza si ottiene facendo una copia separata del valore, non è vero? Questo non rende il passaggio di un tipo di valore potenzialmente costoso?

Bene, sì e no. Non è così brutto come potresti pensare. Come ha detto Nate Cook, il passaggio di un tipo di valore non implica necessariamente la copia, poiché let (esplicitamente o implicitamente) di garantire l’immutabilità, quindi non è necessario copiare nulla. E anche il passaggio a un riferimento var non significa che le cose verranno copiate, solo che possono essere se necessario (perché c’è una mutazione). I documenti specificatamente ti consigliano di non mettere le mutande in giro.

È sempre pass-by-value quando il parametro non è inout .

È sempre pass-by-reference se il parametro è inout . Tuttavia, questo è un po ‘complicato dal fatto che è necessario utilizzare esplicitamente l’operatore & sull’argomento quando si passa a un parametro inout , quindi potrebbe non corrispondere alla definizione tradizionale di pass-by-reference, in cui si passa direttamente la variabile.

Tutto in Swift viene passato per “copia” di default, quindi quando si passa un tipo di valore si ottiene una copia del valore e quando si passa un tipo di riferimento si ottiene una copia del riferimento, con tutto ciò che ciò implica. (Ovvero, la copia del riferimento punta ancora alla stessa istanza del riferimento originale).

Io uso virgolette sulla “copia” sopra perché Swift fa un sacco di ottimizzazione; Ove ansible, non copia fino a quando non c’è una mutazione o la possibilità di mutazione. Poiché i parametri sono immutabili per impostazione predefinita, ciò significa che la maggior parte delle volte non viene eseguita alcuna copia.

Il blog di Apple Swift Developer ha un post chiamato Value and Reference Types che fornisce una discussione chiara e dettagliata su questo argomento.

Per citare:

I tipi in Swift rientrano in una delle due categorie: in primo luogo, “tipi di valore”, in cui ogni istanza conserva una copia univoca dei propri dati, generalmente definita come struct, enum o tupla. Il secondo, “tipi di riferimento”, in cui le istanze condividono una singola copia dei dati e il tipo viene solitamente definito come una class.

Il post sul blog Swift continua a spiegare le differenze con gli esempi e suggerisce quando si userebbe l’uno sull’altro.

Ecco un piccolo esempio di codice per il passaggio per riferimento. Evita di farlo, a meno che tu non abbia una forte ragione per farlo.

 func ComputeSomeValues(_ value1: inout String, _ value2: inout Int){ value1 = "my great computation 1"; value2 = 123456; } 

Chiamalo così

 var val1: String = ""; var val2: Int = -1; ComputeSomeValues(&val1, &val2); 

Le classi vengono passate per riferimento e altre vengono passate per valore in modo predefinito. È ansible passare per riferimento utilizzando la parola chiave inout .