在 swift 中,为什么我不能实例化一个有初始化器的协议?

In swift, why can't I instantiate a protocol when it has an initialiser?

我了解通常我无法实例化协议。 但是如果我在协议中包含一个初始化程序,那么编译器肯定知道当协议被结构或 class 稍后使用时,它会有一个可以使用的初始化? 我的代码如下和行:

protocol Solution {
  var answer: String { get }
}

protocol Problem {
  var pose: String { get }
}

protocol SolvableProblem: Problem {
  func solve() -> Solution?
}

protocol ProblemGenerator {
  func next() -> SolvableProblem
}

protocol Puzzle {
  var problem: Problem { get }
  var solution: Solution { get }

  init(problem: Problem, solution: Solution)
}

protocol PuzzleGenerator {
  func next() -> Puzzle
}

protocol FindBySolvePuzzleGenerator: PuzzleGenerator {
  var problemGenerator: ProblemGenerator { get }
}

extension FindBySolvePuzzleGenerator {
  func next() -> Puzzle {
    while true {
      let problem = problemGenerator.next()
      if let solution = problem.solve() {
        return Puzzle(problem: problem, solution: solution)
      }
    }
  }
}

行:

return Puzzle(problem: problem, solution: solution)

报错:协议类型'Puzzle'无法实例化

当你实例化一个对象时,操作系统必须知道如何在内存中分配和处理这种对象:它是引用类型吗(类)?强引用、弱引用还是无主引用?还是值类型(Structs、Strings、Int 等)?

引用类型存储在堆中,而值类型存在于堆栈中。 Here 彻底解释了两者的区别。

只能实例化引用和值类型(对象)。因此,只有符合该协议的对象才能被实例化,而不是协议本身。协议不是对象,它是对象的某种行为的一般描述或模式。

关于初始化,Apple docs 说的是:

Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.

不幸的是 swift 不允许这样 "hack"

您需要使用 class 来确认该协议作为您引用的对象。

想象协议是形容词。 Movable 说你可以 move 它,Red 说它有 color = "red"... 但他们没有说 是什么.你需要一个名词。一辆红色的可移动汽车。您可以实例化一辆汽车,即使细节不多。您不能实例化红色。

I understand that I can't do it - I just want to understand why the compiler can't do it?

因为 Swift 中的协议代表 abstraction 机制。说到抽象,你可以把它想象成一个模板,我们不必关心它的行为细节或它的属性是什么;因此,能够从中创建对象是没有意义的。

作为一个真实世界的例子,想想我刚才说的 "Table"(作为一个抽象层次),我很确定你会明白我在说什么!尽管如此,我们并没有提及它的细节(例如它的 material 或它有多少条腿......);在某些时候,如果我说 "create a table for me"(实例化一个对象),你会问我关于规格的问题!这就是编译器不允许您直接从协议创建对象的原因。这就是让事情变得抽象的意义所在。

此外,检查:Why can't an object of abstract class be created? 可能会有帮助。

But if I include an initialiser in the protocol then surely the compiler knows that when the protocol is used by a struct or class later, it will have an init which it can use?

classes 必须采用协议,并且可能有十几种不同的 classes 都采用您的 Puzzle 协议。编译器不知道要实例化哪些 classes。

协议使我们能够在没有多重继承的复杂性的情况下组合接口。在像 C++ 这样的多重继承语言中,您必须处理这样一个事实,即单个 class D 可能继承自另外两个 classes,BC ],而这两个 classes 可能碰巧有同名的方法或实例变量。如果他们都有一个methodA(),而B::methodA()C::methodA()是不同的,当有人称D的继承methodA()时,你用哪个?更糟糕的是,如果 BC 都来自一个共同的基数 class A 怎么办?协议通过不直接实例化来避免很多这种情况,同时仍然提供使多重继承具有吸引力的接口多态性。