我有下一个继承:
protocol P {
var a : Int { get set }
}
class C : P {
var a : Int
...
}
然后我想创建通用数组并通过一些动作迭代它:
class Main {
var array : Array<Proto>
var inst : Proto
...
func foo() {
array.append(C(a:10))
for obj in array {
obj.a = 20 //Error: Cannot assign to the result of this expression
}
inst = C(a:10)
inst.a = 20 //Works correctly
for var i = 0; i < arr.count; ++i {
inst = arr[i]
inst.a = 20 //No problem even here
}
}
}
如果我演员:( obj为C).a = 20 - 那么一切都OK。 有人可以解释这种行为吗?
答案 0 :(得分:1)
问题是for-in循环不允许修改迭代项。来自documentation:
请注意
索引常量仅存在于循环范围内。如果要在循环完成后检查索引的值,或者如果要将其值作为变量而不是常量使用,则必须在循环中使用之前自行声明它。
您可以尝试以下操作:
for obj in array {
var c = obj
c.a = 20 // This should work as c is a mutable copy of obj
}
<强>补充:强>
注意@ikuramedia说的是正确的。上面的代码掩盖了obj
是值类型的情况,在这种情况下,c
将是其副本,而数组中的值obj
实际上不会被修改。
答案 1 :(得分:1)
协议可以同等地应用于类,结构和枚举,因此在您的代码中,编译器不能假定obj是对象类型。因此,它不知道obj是引用类型还是值类型。如果它是一个值类型,那么你不能在没有变异方法的情况下改变对象,因此它假定你的代码类型不正确并且无法编译。
您可以将数组声明为符合Proto的AnyObject类型,也可以在for循环迭代器中向下转换:for obj as! C in array
答案 2 :(得分:1)
其他人已经描述过这个问题 - 总结一下:
如果协议由结构调整,则赋值将失败,因为我们无法分配常量结构的属性(并且迭代器obj
是常量)。
有两种方法可以解决这个问题:
将协议声明为类协议,以便结构不能对其进行调整:
protocol P : class {
var a : Int { get set }
}
这将确保符合P
的任何对象必须是引用类型。
修改迭代以适用于结构和类
for i in indices(array) {
array[i].a = 20
}
现在我们可以修改属性,无论元素是值还是引用类型。但请注意,此类迭代仅在您就地修改元素的内容时才有效。插入和删除将使indices(array)
返回的索引无效。
此外,您必须注意确保您正在修改内容。具体来说,当元素是值类型时,这样的代码实际上不会修改数组的内容:
for i in indices(array) {
var element = array[i]
element.a = 20
}