本文介绍了为什么我不能在 self 是一个类的协议扩展中更改变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很好奇为什么这不起作用:

I am curious why this doesn't work:

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.如果我在 MyProtocol 后面添加 : class 声明,它会起作用.我不明白为什么它不适用于子协议.

Why? Only classes can adopt MyProtocol2. If I add : class declaration behind MyProtocol it works. I do not understand why it doesn't work on a subprotocol.

推荐答案

您的示例无法编译,因为 MyProtocol 不是类绑定的,因此可以具有 mutating 要求和扩展成员.这包括属性设置器,默认情况下它们是 mutating.这些成员可以自由地为 self 重新分配一个全新的值,这意味着编译器需要确保在可变变量上调用它们.

Your example doesn't compile because MyProtocol isn't class-bound, and as such can have mutating requirements and extension members. This includes property setters, which are by default mutating. Such members are free to re-assign a completely new value to self, meaning that the compiler needs to ensure they're called on mutable variables.

例如,考虑:

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 是不可变的,因此代码的格式不是很好.

If this were legal, calling c.a() would re-assign a completely new instance of C to the variable c. But c is immutable, therefore the code is not well formed.

使 MyProtocol 类绑定(即 protocol MyProtocol : AnyObject 或弃用的拼写 protocol MyProtocol : class)有效,因为现在编译器知道只有类可以符合 MyProtocol.因此,它通过禁止 mutating 要求和扩展成员来强加引用语义,从而防止 self 的任何变化.

Making MyProtocol class bound (i.e protocol MyProtocol : AnyObject or the deprecated spelling protocol MyProtocol : class) works because now the compiler knows that only classes can conform to MyProtocol. Therefore it imposes reference semantics by forbidding mutating requirements and extension members and therefore prevents any mutations of self.

您可以使用的另一种选择是将需求 i 的 setter 标记为 nonmutating - 因此这意味着它只能由非变异 setter 来满足.这使您的代码再次格式良好:

Another option at your disposal is to mark the setter for the requirement i as being nonmutating – therefore meaning that it can only be satisfied by a non-mutating setter. This makes your code once again well-formed:

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
  }
}

这篇关于为什么我不能在 self 是一个类的协议扩展中更改变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-26 22:02