能否为Swift结构体数组元素创建引用?寻求替代类的方案
先看一段典型的Swift结构体数组代码:
import Foundation struct Card { var flag: String = "" } var cards = Array<Card>() cards.append(Card())
问题1:直接取数组元素是副本,无法修改原数组
因为结构体是值类型,直接取出数组元素赋值给变量时,拿到的是原元素的副本,修改这个变量只会改动副本,原数组元素完全不受影响:
// 创建一个副本 var cardCopy = cards[0] // 不会修改cards[0] cardCopy.flag = "modify0" print(cards[0].flag) // 输出空字符串
问题2:直接索引修改可行但效率低
虽然可以直接通过数组索引修改原元素,但如果要多次修改同一个元素的多个属性,每次都写cards[0].xxx不仅代码冗余,还会重复执行数组的索引访问操作:
// 可以修改原数组元素,但单次操作没问题 cards[0].flag = "modify" print(cards[0].flag) // 输出"modify" // 多次修改同一个元素的多个属性时,代码繁琐且重复索引访问 cards[0].flag0 = "modify" cards[0].flag1 = "modify" cards[0].flag2 = "modify" cards[0].flag3 = "modify" // ...
我们希望能创建一个类似引用的变量,通过这个变量直接修改原数组中的元素,不用每次都写索引,就像这样:
// 期望的写法:拿到cards[0]的"引用" var cardReference = ... cardReference.flag0 = "modify" cardReference.flag1 = "modify" cardReference.flag2 = "modify" cardReference.flag3 = "modify" // ...
而且不想直接把结构体改成类(虽然类是引用类型可以解决,但想探索其他方案)。
替代方案1:用inout函数封装批量修改逻辑
可以写一个接受inout参数的函数,在函数内部一次性修改结构体的多个属性,这样只需要一次索引传递就能完成所有修改:
func updateCard(_ card: inout Card) { card.flag0 = "modify" card.flag1 = "modify" card.flag2 = "modify" card.flag3 = "modify" } // 调用时只需要一次索引访问,传入原数组元素的"引用" updateCard(&cards[0])
这种方式利用了Swift的inout参数特性,函数内部对参数的修改会直接作用到原数组的元素上,避免了多次索引访问的冗余操作。
替代方案2:用带索引的闭包封装修改逻辑
如果不想单独写函数,也可以用闭包来一次性完成修改,同样只需要做一次数组索引校验和访问:
// 直接对指定索引的元素做批量修改 modifyCard(at: 0) { card in card.flag0 = "modify" card.flag1 = "modify" card.flag2 = "modify" card.flag3 = "modify" } // 封装的通用闭包函数 func modifyCard(at index: Int, update: (inout Card) -> Void) { guard index < cards.count else { return } update(&cards[index]) }
这个方案更灵活,可以根据不同的修改需求传入不同的闭包逻辑,同时保证只做一次数组边界校验。
替代方案3:用UnsafeMutablePointer获取指针(不推荐常规场景)
如果追求极致的底层操作,可以用指针来直接访问数组元素,但这种方式不安全,容易出现越界、内存失效等问题,只建议在性能极度敏感且能保证操作安全的场景下使用:
// 获取数组第一个元素的指针 guard let cardPtr = cards.withUnsafeMutableBufferPointer({ $0.baseAddress }) else { return } // 通过指针修改原元素 cardPtr.pointee.flag0 = "modify" cardPtr.pointee.flag1 = "modify" cardPtr.pointee.flag2 = "modify" cardPtr.pointee.flag3 = "modify"
⚠️ 注意:使用指针时必须确保数组在操作期间不会被重新分配内存(比如数组扩容),否则指针会失效导致崩溃。
总结一下:如果不想把结构体改成类,最安全且优雅的方式是用inout函数或者闭包封装批量修改逻辑,既避免了多次索引访问的冗余,又能直接修改原数组元素。
内容的提问来源于stack exchange,提问作者Cheok Yan Cheng

