Swift 4.2)使用for_in / forEach与按索引访问对结构体数组进行突变

时间:2018-10-11 23:55:05

标签: arrays swift swift-structs

我正在尝试修改数组中的struct元素。我发现可以通过按索引访问(迭代)该结构来实现,但是如果使用“ 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
}

有人可以解释吗?

2 个答案:

答案 0 :(得分:3)

如其他答案所述,您不能在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对于这些情况似乎进行了优化。 Time Profiler报告说,第二种方法的操作速度快了NEAR 2倍,数组选项为1 000 000 Person s。

如果您确实希望使用.forEach方法的变异副本,请在MutableCollection上使用扩展名:

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 }

答案 1 :(得分:1)

在Swift中,结构是类型。在for或foreach循环中,人员项目是一个值,如果可变,则您将只更改原始副本,而不是按预期更改原始副本。

如果您确实想要在循环内对 struct 进行可更新的引用,请添加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