Swift - 如何在迭代过程中修改结构体对象

49

我仍然不确定struct复制或引用的规则。

我想在迭代数组时改变一个结构对象的属性:例如,在这种情况下,我想改变背景颜色,但编译器却报错了。

struct Options {
  var backgroundColor = UIColor.blackColor()
}

var arrayOfMyStruct = [MyStruct]

...

for obj in arrayOfMyStruct {
  obj.backgroundColor = UIColor.redColor() // ! get an error
}
7个回答

61

struct是值类型,因此在for循环中,您正在处理其副本。

只是作为一个测试,您可以尝试这个:

Swift 3:

struct Options {
   var backgroundColor = UIColor.black
}

var arrayOfMyStruct = [Options]()

for (index, _) in arrayOfMyStruct.enumerated() {
   arrayOfMyStruct[index].backgroundColor = UIColor.red
}

Swift 2:

struct Options {
    var backgroundColor = UIColor.blackColor()
}

var arrayOfMyStruct = [Options]()

for (index, _) in enumerate(arrayOfMyStruct) {
    arrayOfMyStruct[index].backgroundColor = UIColor.redColor() 
}

在这里,您只需枚举索引,直接访问存储在数组中的值。

希望这可以帮助到您。


我有一个澄清的问题:如果您为当前的MyStruct对象指定了名称,并且由于数组是按值传递的,那么在下一行尝试打印命名的结构体对象的backgroundColor将导致打印旧值,对吗? - dudeman
1
这对于Swift 3不起作用,请参见我下面针对Swift 3的答案。 - ldoogy

28
你可以使用 Array.indices
for index in arrayOfMyStruct.indices {
    arrayOfMyStruct[index].backgroundColor = UIColor.red
}

11
注意来自 Apple 文档的讨论:一个集合的 indices 属性可能会对集合本身持有强引用,导致集合被非唯一引用。如果在迭代其 indices 时更改集合,强引用可能会导致意外复制集合。为避免意外复制,请使用 index(after:) 方法从 startIndex 开始生成索引。 - Mihai Erős
3
@Mihai Erös评论中的链接已经更改为:https://developer.apple.com/documentation/swift/string/2949967-indices。 - jvarela
1
@MihaiErős 这个也适用于 for (index, _) in arrayOfMyStruct.enumerated() 吗? - cumanzor
正确的文档页面链接:https://developer.apple.com/documentation/swift/collection/1641719-indices - Ralf Ebert
枚举(enumerated())文档中没有提到这方面的内容。另外,出于清晰起见,我仍然会使用.indices;但是如果你正在处理一个非常大的列表,注意一下这个问题是很好的。 - Ralf Ebert
strong reference警告现在只是字符串集合(https://developer.apple.com/documentation/swift/string/indices-swift.property)的一部分,而不是通用的https://developer.apple.com/documentation/swift/array/indices。 - undefined

14

您正在使用 struct 对象,当使用 for in 循环时,这些对象将会被复制到本地变量中。此外,数组也是一个 struct 对象,因此如果您想要改变数组的所有成员,您需要创建修改过的原始数组的副本,该副本由修改过的原始对象的副本填充。

arrayOfMyStructs = arrayOfMyStructs.map { el in
   var el = el
   el.backgroundColor = .red
   return el
}

通过添加这个数组扩展,它可以变得更简单。

Swift 4/5

extension Array {
    mutating func mutateEach(by transform: (inout Element) throws -> Void) rethrows {
        self = try map { el in
            var el = el
            try transform(&el)
            return el
        }
     }
}

用法

arrayOfMyStructs.mutateEach { el in
    el.backgroundColor = .red
}

5
对于Swift 3,使用enumerated()方法。 例如:
for (index, _) in arrayOfMyStruct.enumerated() {
  arrayOfMyStruct[index].backgroundColor = UIColor.redColor() 
}

该元组还包括对象的副本,因此您可以使用for(index,object)直接访问对象,但由于它是一个副本,您将无法以这种方式更改数组,并且应该使用index来进行更改。为了直接引用文档

如果您需要每个项目的整数索引以及其值,请使用enumerated()方法遍历数组。对于数组中的每个项目,enumerated()方法返回一个元组,由整数和项目组成。


1
如果你只需要索引,那么就不需要使用枚举方法。只需使用for i in 0..< arrayOfMyStruct.count即可。 - Peter Schorn

1

另一种不需要每次编写下标表达式的方法。

struct Options {
    var backgroundColor = UIColor.black
}

var arrayOfMyStruct = [Options(), Options(), Options()]

for index in arrayOfMyStruct.indices {
    var option: Options {
        get { arrayOfMyStruct[index] }
        set { arrayOfMyStruct[index] = newValue }
    }
    option.backgroundColor = .red
}

0

这是我的示例代码

arrayOfMyStruct.indices.forEach { arrayOfMyStruct[$0].backgroundColor=UIColor.red }

-8

我在一些代码中看到了这个方法,它似乎是有效的

for (var mutableStruct) in arrayOfMyStruct {
  mutableStruct.backgroundColor = UIColor.redColor()
}

6
“var”关键字使得您正在迭代的项在本地可变(但不会更改支持结构)。 - Entalpi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接