【问题标题】:Is it ever possible to create a reference to element of array of struct?是否有可能创建对结构数组元素的引用?
【发布时间】:2020-12-08 15:28:45
【问题描述】:

给定一个Arraystruct

    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。但是,在此之前,我想探索其他替代方案。

【问题讨论】:

  • 如果你需要修改你的 Card 实例很多,也许你应该把它变成一个类,或者用新值创建一个新实例并替换数组中的整个对象。
  • @JoakimDanielson 谢谢。在将其从结构转换为类之前,我想探索其他可能的替代方案(可能创建引用?)。
  • 您不能将值类型转换为引用类型。正如其他人指出的那样,inout 与您将要获得的一样接近。

标签: ios swift struct reference


【解决方案1】:

您可以使用函数进行更改并通过引用传递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 = ""
}

那么你就可以像上面一样使用它了。

【讨论】:

  • 我真的很喜欢这种使结构通常可以自我更新的技巧;我不敢相信我从来没有想过这个。 :)
  • 我想把这个想法写进我的书中(当然归功于你);如果可以,请告诉我。
  • @matt 我做了一些研究,关于我第一次看到这样的东西并发现这个gist.github.com/nicklockwood/9b4aac87e7f88c80e932ba3c843252df。所以,对我来说这当然没问题,但公平地说,这并不完全是我的想法。
  • 好吧,你的想法并不是真正取自那个要点,所以我仍然认为你得到了信任。
  • @matt 好吧,是的,这就像相同技术的另一种用法。
【解决方案2】:

这是预期的行为,因为Arraystructstructs 是值类型。

您需要的是引用类型行为,因此您应该将您的 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")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    相关资源
    最近更新 更多