9

我正在尝试修改数组中的结构元素。我发现您可以通过按索引访问(迭代)结构来做到这一点,但如果您使用“for in”循环或 forEach{},则不能。

struct Person
{
  var age = 0
  var name = "James"
}

var personArray = [Person]()
personArray += [Person(), Person(), Person()]


personArray.forEach({$0.age = 10}) // error: "Cannot assign to property: '$0' is immutable"

for person in personArray { 
  person.age = 10 // error: "Cannot assign to property: 'person' is a 'let' constant"
}


for index in personArray.indices {
  personArray[index].age = 10 // Ok
}

有人可以解释吗?

4

2 回答 2

16

如其他答案所述,您不能在for-in循环或.forEach方法中进行变异。

您可以使用最后一个简洁明了的公式:

for index in personArray.indices {
    personArray[index].age = 10
}

或者完全改变原件personArray

personArray = personArray.map { person in 
    var person = person // parameter masking to allow local mutation
    person.age = 10
    return person
}

请注意,第二个选项似乎效率较低,因为它Person每次都会创建一个新实例,但 Swift 似乎针对这些情况进行了很好的优化。时间分析器报告说,第二个选项的操作速度提高了近 2 倍,数组为 1 000 000Person秒。

如果您真的想要.forEach方法的变异对应物,这是一个奖励:

extension MutableCollection {
    mutating func mutateEach(_ body: (inout Element) throws -> Void) rethrows {
        for index in self.indices {
            try body(&self[index])
        }
    }
}

这是一个与您的第一个公式等效的数组突变的包装器,您可以按预期使用它:

personArray.mutateEach { $0.age = 10 }
于 2020-01-17T16:34:40.577 回答
5

在 Swift 中,结构是一种类型。在 for 或 foreach 循环中,person 项是一个值,如果它是可变的,则您只会更改原始副本,而不是您想要的原始副本。

如果您真的想要循环内的结构的可更新引用,请添加一个 var 关键字,但请记住您更新的是副本而不是原始副本。

for var person in personArray {
    person.age = 10 // updates a copy, not the original
}

相比之下,类是一个引用类型,在循环中每个项目都是对原始的引用。更新此参考值现在会更新原始值。

将 Person 的定义更改为 class 而不是 struct ,它将按预期工作。有关完整说明,请参阅https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html

于 2018-10-12T00:22:37.673 回答