简体   繁体   English

Swift 4.2) 使用 for_in/forEach 改变结构数组与按索引访问

[英]Swift 4.2) Mutate array of struct with for_in/forEach vs. access by index

I am trying to modify struct element in array.我正在尝试修改数组中的结构元素。 I found that you can do that by accessing(iterate) the struct by index, but you can't if you use 'for in' loop or forEach{}.我发现您可以通过按索引访问(迭代)结构来做到这一点,但如果您使用“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
}

Can someone explain?有人可以解释一下吗?

As stated in other answers you can't mutate in a for-in loop or in a .forEach method.正如其他答案中所述,您不能在for-in循环或.forEach方法中进行.forEach

You can either use you last formulation that is short and concise:您可以使用简短而简洁的最后一个公式:

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

Or mutate the orignal personArray entirely:或者完全改变原始的personArray

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

Note that the second option may seems less efficient as it creates a new instance of Person each time but Swift seems to be well optimised for those cases.请注意,第二个选项似乎效率较低,因为它每次都创建一个新的Person实例,但 Swift 似乎针对这些情况进行了很好的优化。 Time profiler reported NEAR 2x faster operation for the second option with an array of 1 000 000 Person s.时间分析器报告说,第二个选项的操作速度提高了近 2 倍,其中包含 1 000 000 Person的数组。

Here is a bonus if you really want a mutating counterpart for .forEach method, use an extension on MutableCollection :如果你真的想要.forEach方法的变异对应物,这里有一个奖励,在MutableCollection上使用扩展:

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

This is a wrapper around array mutation equivalent to your first formulation, you can use it like intended:这是一个相当于您的第一个公式的数组突变的包装器,您可以按预期使用它:

personArray.mutateEach { $0.age = 10 }

In Swift a struct is a value type.在 Swift 中,结构体是一种类型。 In the for or foreach loop the person item is a value, and if it were mutable you would only be changing a copy of the original, not the original as you intend.在 for 或 foreach 循环中,person 项是一个值,如果它是可变的,您将只更改原始副本,而不是您想要的原始副本。

If you really want an updatable reference to the struct inside the loop add a var keyword, but remember you are updating a copy not the original.如果您真的想要对循环内结构的可更新引用,请添加 var 关键字,但请记住,您正在更新副本而不是原始副本。

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

By contrast class is a reference type and in the loop each item is a reference to the original.相比之下, class 是一种引用类型,在循环中,每一项都是对原始项的引用。 Updating this reference value now updates the original.更新此参考值现在会更新原始值。

Change your definition of Person to class instead of struct and it will work as expected.将 Person 的定义更改为 class 而不是 struct ,它将按预期工作。 For a complete explanation see https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html有关完整说明,请参阅https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM