在协议中指定一个可设置的 property/variable

Specify a settable property/variable in a protocol

我希望我的协议声明有 read/write 属性 可用。我已经尝试过了,但这不起作用:

protocol EdibleThing {
  var eaten: Bool { get set }
}

class Pickle: EdibleThing { var eaten = false }
class RusticGrapefruit: EdibleThing { var eaten = false }

class Jar {
  let contents: [EdibleThing] = [Pickle(), RusticGrapefruit()]
  var nextItem: EdibleThing {
    return contents.last ?? Pickle() // Lazy pickle generation technology
  }
  func eat() {
    let food = nextItem
    food.eaten = true // (!) ERROR: Cannot assign to result of this expression
  }
}

我做错了什么?我想我已经声明协议有一个名为 eaten 的 get/set 变量,那么为什么我不能设置它?

你正在变异 food

let food = nextItem 替换为 var food = nextItem

该协议可能由 classes 和结构实现 - 这会阻止您更改 class 实例的内部状态或使用不可变变量实现该协议的结构。

要解决此问题,您必须将 food 变量声明为可变的:

func eat() {
    var food = nextItem
    food.eaten = true // (!) ERROR: Cannot assign to result of this expression
}

或声明 EdibleThing 协议只能由 classes 实现:

protocol EdibleThing : class {
    var eaten: Bool { get set }
}  

请注意,发生这种情况是因为 foodEdibleThing 类型的变量 - 编译器不知道实际实例是值类型还是引用类型,因此会引发错误。如果你让它成为一个 class 类型的变量,像这样:

let food: Pickle = nextItem as! Pickle

编译器毫不含糊地知道它是一个引用类型,在这种情况下它允许赋值。但我想这会破坏你的应用程序逻辑......所以把它当作一个例子

问题是您不能在 let.

定义的值类型上改变 属性

即使 RusticGrapefruitPickle 都是 class 实现(引用类型),协议也可以像结构一样分配给值类型。编译器检测到潜在问题并阻止我们。

两种解决方案:

  1. let 更改为 var(在我的例子中,这意味着更改大量引用此类对象的代码。此外,我喜欢语义值和可能的编译器优化let)
  2. 声明协议仅对 classes 有效:protocol EdibleThing: class { }