forEach 导致 $0 是不可变错误

forEach results in $0 is immutable error

提问人:meaning-matters 提问时间:9/5/2016 最后编辑:meaning-matters 更新时间:9/1/2022 访问量:18729

问:

protocols.forEach { $0.prop = nil }

结果如下:

Cannot assign to property: '$0' is immutable

我解决了这个问题:

protocols.forEach
{
    var protocol = $0

    protocol.prop = nil
}

但是为什么编译器可以接受呢?我希望它能解决这个问题。

数组 swift foreach

评论

0赞 vacawama 9/5/2016
对象是结构还是类的实例?
0赞 meaning-matters 9/5/2016
@vacawama 不,谢谢你的提问,他们实际上是。protocol
0赞 vacawama 9/5/2016
如果实现协议的所有内容都是一个类,则添加到您的协议中,它将起作用。": class"
0赞 meaning-matters 9/5/2016
@vacawama 该协议来自其他人开发/维护的框架,所以我不想只是一个.: class
0赞 vacawama 9/5/2016
没关系。至少现在您知道为什么必须执行变通方法,并且了解只有当数组中的项是 .varclass

答:

58赞 vacawama 9/5/2016 #1

您有一组实现协议的项。如果你不告诉 Swift 这是一个协议(或早期版本的 Swift 中的协议),它将假定它可以由 .AnyObjectclassstruct

在循环中,您基本上将依次将一个变量(默认调用)分配给数组中的每个值。如果有一个对象数组(实例 ),则可以修改该项的属性。如果你有一个项目数组,那么这些项目将是不可变的。如果你有一个实现协议的项数组,那么 Swift 会将它们视为限制性更强的项,除非你添加(或在早期版本的 Swift 中)到你的协议定义中。forEachlet$0classstructstruct: AnyObject: class

例如:

protocol Xyzzy: AnyObject {
    var prop: Int? { get set }
}

class Fred: Xyzzy, CustomStringConvertible {
    var description: String { String(describing: prop) }
    var prop: Int? = 17
}

let objects: [Xyzzy] = [Fred(), Fred(), Fred()]

print(objects)  //  [Optional(17), Optional(17), Optional(17)]

objects.forEach { $0.prop = nil }

print(objects)  // [nil, nil, nil]

解决方法:

protocols.forEach
{
    var protocol = $0

    protocol.prop = nil
}

适用于对象,因为它创建指向对象的新指针,然后允许您修改对象。请注意,此解决方法仅适用于类的实例。如果您的协议是由 实现的,则 new 将是该项的新副本,并且数组中的原始副本不会更改。classvarstructvarstruct

评论

2赞 meaning-matters 9/5/2016
很好的答案,有助于更深入地了解 Swift。谢谢!
0赞 0xxxD 5/14/2019
那么为什么会这样呢?还有更深层次的原因吗? 像内存布局/编译器改进/结构体这样的东西是 swift 中的值类型?
2赞 vacawama 5/14/2019
@0xxxD,在 Swift 中,所有类型在编译时都是已知的。结构体和类可以采用的协议被视为限制性更强的结构体,因为 Swift 不会在运行时做出动态决策。是的,结构是值类型。当它们被分配给 let 常量(如循环变量)时,所有属性都是不可变的。声明一个协议告诉 Swift,你只会将它与类一起使用,而 Swift 在编译时会将该协议类型的变量视为引用。class
0赞 adamjansch 9/1/2022
截至 2021 年 (?),该协议需要而不是 ,因为后者已被弃用。AnyObjectclass
1赞 vacawama 9/1/2022
@adamjansch,感谢您帮助我更新此答案!
27赞 Denis Rybkin 2/17/2021 #2

如何更改ArrayStructs

对于每个元素:

itemsArray.indices.forEach { itemsArray[$0].someValue = newValue }

对于特定元素:

itemsArray.indices.filter { itemsArray[$0].propertyToCompare == true }
                  .forEach { itemsArray[$0].someValue = newValue }

评论

2赞 mushcraft 9/15/2021
我几乎用过这个,但我不得不评论未来的自己或其他人发生了什么。感觉也没有那么快。所以发现有作为序列协议的一部分。我用了它。 Map 现在将有两个默认参数:$0 是索引,$1 是值。或enumerateditemsArray.enumerated().mapitemsArray.enumerated().map{ index, value in //morecode }
0赞 steveSarsawa 2/10/2023
脱帽致敬,你节省了我的时间:)