Можно ли когда-нибудь создать ссылку на элемент массива структуры?

Aug 19 2020

Учитывая Arrayсstruct

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

Следующая операция НЕ изменит исходный элемент массива

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

Следующая операция изменит исходный элемент массива

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

    print(cards[0].flag)

Однако это неэффективно в том смысле, что нам нужно каждый раз выполнять доступ к индексации. Представить

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

Есть ли способ создать ссылку на элемент массива структуры? Чтобы мы могли написать

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

Одна из возможностей - заменить structна class. Но, прежде чем делать это, я хотел бы изучить другую альтернативу.

Ответы

7 gcharita Aug 19 2020 at 11:25

Вы можете добиться этого, используя функцию для внесения изменений и передачи Cardструктуры по ссылке следующим образом:

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

или даже лучше, используя функцию мутации в Cardтипе и передавая в качестве закрытия свои изменения следующим образом:

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

Обновление : чтобы сделать второй подход еще более пригодным для повторного использования, вы можете определить такой протокол:

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

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

и сделайте Cardструктуру соответствующей ему:

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

Затем вы можете использовать его, как указано выше.

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

Это ожидаемое поведение, поскольку Arrayis a structи structs являются типами значений.

Что вам нужно, так это поведение ссылочного типа, поэтому вам следует преобразовать structего в class.

Еще одно решение для изменения нескольких свойств типа значения за один раз - создать mutatingфункцию, которая делает это и вызывает ее в элементе массива.

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