É possível criar uma referência ao elemento da matriz de estrutura?

Aug 19 2020

Dado um Arraycomstruct

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

A operação a seguir NÃO modificará o elemento original da matriz

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

A seguinte operação irá modificar o elemento original da matriz

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

    print(cards[0].flag)

No entanto, não é eficiente no sentido de que precisamos executar o acesso de indexação a cada vez. Imagine

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

Existe uma maneira, podemos criar referência ao elemento do array de struct? Para que possamos escrever

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

Uma das possibilidades é substituir structpor class. Mas, eu gostaria de explorar outra alternativa, antes de fazer isso.

Respostas

7 gcharita Aug 19 2020 at 11:25

Você pode conseguir isso usando uma função para fazer suas alterações e passar a Cardestrutura por referência desta forma:

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

ou melhor ainda, usando uma função mutante no Cardtipo e passar como um encerramento suas alterações como esta:

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

Atualização : para tornar a segunda abordagem ainda mais reutilizável, você pode definir um protocolo como este:

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

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

e fazer com que a Cardestrutura esteja em conformidade com ela:

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

Então você pode usá-lo como acima.

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

Este é o comportamento esperado, uma vez que Arraya structe structs são tipos de valor.

O que você precisa é um comportamento de tipo de referência, portanto, você deve converter seu structpara um class.

Outra solução para modificar várias propriedades de um tipo de valor de uma vez é criar uma mutatingfunção que faça isso e chamar isso no elemento da matriz.

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