代码之家  ›  专栏  ›  技术社区  ›  J. Doe

为什么我不能在self是类的协议扩展中更改变量?

  •  9
  • J. Doe  · 技术社区  · 6 年前

    我很好奇为什么这样不行:

    public protocol MyProtocol {
        var i: Int { get set }
    }
    
    public protocol MyProtocol2: class, MyProtocol {}
    
    public extension MyProtocol2 where Self: AnyObject {
        func a() {
            i = 0 <-- error
        }
    }
    

    错误:

    无法分配给属性:“self”是不可变的

    为什么?只有类可以采用MyProtocol2。如果我加上 : class 在MyProtocol后面声明它有效。我不明白为什么它不适用于子目录。

    2 回复  |  直到 6 年前
        1
  •  9
  •   Hamish    6 年前

    您的示例无法编译,因为 MyProtocol 不受类约束,因此可以 mutating 要求和扩展成员。这包括默认的属性设置器 变异 . 这些成员可以自由地重新分配 全新的 价值 self ,这意味着编译器需要确保对可变变量调用它们。

    例如,考虑:

    public protocol MyProtocol {
      init()
      var i: Int { get set } // implicitly `{ get mutating set }`
    }
    
    extension MyProtocol {
      var i: Int {
        get { return 0 }
        // implicitly `mutating set`
        set { self = type(of: self).init() } // assign a completely new instance to `self`.
      }
    }
    
    public protocol MyProtocol2 : class, MyProtocol {}
    
    public extension MyProtocol2 where Self : AnyObject {
      func a() {
        i = 0 // error: Cannot assign to property: 'self' is immutable
      }
    }
    
    final class C : MyProtocol2 {
      init() {}
    }
    
    let c = C()
    c.a()
    

    如果这是合法的,打电话 c.a() 将重新分配 C 到变量 c . 但是 c类 是不可变的,因此代码的格式不正确。

    制作 我的协议 类绑定(即 protocol MyProtocol : AnyObject 或者不赞成的拼写 protocol MyProtocol : class )因为现在编译器知道只有类才能符合 我的协议 . 因此它通过禁止 变异 需求和扩展成员,因此可以防止 自己 .

    另一个你可以选择的方法是为需求标记setter i 作为存在 nonmutating 因此,这意味着它只能由一个非变异的setter来满足。这使您的代码再次形成良好的格式:

    public protocol MyProtocol {
      init()
      var i: Int { get nonmutating set }
    }
    
    public protocol MyProtocol2 : class, MyProtocol {}
    
    public extension MyProtocol2 where Self : AnyObject {
      func a() {
        i = 0 // legal
      }
    }
    
        2
  •  0
  •   Cristik    6 年前

    属性设置器在被调用方上触发变异,这意味着对该属性进行操作的任何方法都需要声明为 mutating :

    public extension MyProtocol2 where Self: AnyObject {
        mutating func a() {
            i = 0
        }
    }
    

    这将允许在 self 在方法内。