协议类型对象的Swift数组

时间:2014-06-25 09:05:33

标签: inheritance swift dynamic-linking

我有下一个继承:

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。 有人可以解释这种行为吗?

3 个答案:

答案 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)

其他人已经描述过这个问题 - 总结一下:

  1. 迭代器始终是常量
  2. 协议可以通过类和结构进行调整
  3. 如果协议由结构调整,则赋值将失败,因为我们无法分配常量结构的属性(并且迭代器obj是常量)。

    有两种方法可以解决这个问题:

    1. 将协议声明为类协议,以便结构不能对其进行调整:

      protocol P : class {
          var a : Int { get set }
      }
      

      这将确保符合P的任何对象必须是引用类型。

    2. 修改迭代以适用于结构和类

      for i in indices(array) {
          array[i].a = 20
      }
      

      现在我们可以修改属性,无论元素是值还是引用类型。但请注意,此类迭代仅在您就地修改元素的内容时才有效。插入和删除将使indices(array)返回的索引无效。

      此外,您必须注意确保您正在修改内容。具体来说,当元素是值类型时,这样的代码实际上不会修改数组的内容:

      for i in indices(array) {
          var element = array[i]
          element.a = 20
      }