È mai possibile creare un riferimento a un elemento di array di struct?

Aug 19 2020

Dato un Arrayconstruct

    import Foundation
    
    struct Card {
        var flag: String = ""
    }
    
    var cards = Array<Card>()
    cards.append(Card())

La seguente operazione NON modificherà l'elemento dell'array originale

    // A copy is created.
    var cardCopy = cards[0]
    
    // Will NOT modify cards[0] 
    cardCopy.flag = "modify0"
    
    print(cards[0].flag)

La seguente operazione modificherà l'elemento dell'array originale

    // We can modify cards[0] by
    cards[0].flag = "modify"

    print(cards[0].flag)

Tuttavia, non è efficiente nel senso, dobbiamo eseguire ogni volta l'accesso all'indicizzazione. Immaginare

    cards[0].flag0 = "modify"
    cards[0].flag1 = "modify"
    cards[0].flag2 = "modify"
    cards[0].flag3 = "modify"
    ...

C'è un modo, possiamo creare un riferimento all'elemento dell'array di struct? In modo che possiamo scrivere

// How to create a reference to cards[0]?
var cardReference = ...
    cardReference.flag0 = "modify"
    cardReference.flag1 = "modify"
    cardReference.flag2 = "modify"
    cardReference.flag3 = "modify"
    ...

Una delle possibilità è sostituire structcon class. Ma vorrei esplorare altre alternative, prima di farlo.

Risposte

7 gcharita Aug 19 2020 at 11:25

Puoi ottenerlo usando una funzione per apportare le tue modifiche e passare la Cardstruttura per riferimento in questo modo:

func update(card: inout Card) {
    card.flag0 = "modify"
    card.flag1 = "modify"
    card.flag2 = "modify"
    card.flag3 = "modify"
}

var cards = Array<Card>()
cards.append(Card())

update(card: &cards[0])

o ancora meglio usando una funzione mutante nel Cardtipo e passa come chiusura le tue modifiche in questo modo:

struct Card {
    var flag0: String = ""
    var flag1: String = ""
    var flag2: String = ""
    var flag3: String = ""
    
    mutating func update(block: (inout Card) -> Void) {
        block(&self)
    }
}
    
var cards = Array<Card>()
cards.append(Card())

cards[0].update {
    $0.flag0 = "modify" $0.flag1 = "modify"
    $0.flag2 = "modify" $0.flag3 = "modify"
}

Aggiornamento : per rendere il secondo approccio ancora più riutilizzabile, puoi definire un protocollo come questo:

protocol Updatable {
    mutating func update(block: (inout Self) -> Void)
}

extension Updatable {
    mutating func update(block: (inout Self) -> Void) {
        block(&self)
    }
}

e rendi la Cardstruttura conforme ad essa:

struct Card: Updatable {
    var flag0: String = ""
    var flag1: String = ""
    var flag2: String = ""
    var flag3: String = ""
}

Quindi puoi usarlo proprio come sopra.

DávidPásztor Aug 19 2020 at 11:16

Questo è il comportamento previsto, poiché Arrayè a structe structs sono tipi di valore.

Ciò di cui hai bisogno è il comportamento del tipo di riferimento, quindi dovresti convertire il tuo structin un file class.

Un'altra soluzione per modificare diverse proprietà di un tipo di valore in una volta è creare una mutatingfunzione che lo faccia e chiamarla sull'elemento dell'array.

struct Card {
    var flag: String = ""
    var flag2 = ""

    mutating func update(flag: String, flag2: String) {
        self.flag = flag
        self.flag2 = flag2
    }
}

var cards = [Card(flag: "a", flag2: "b")]
cards[0].update(flag: "b", flag2: "c")