在 swift 中,为什么当协议有初始化器时,我不能实例化它?



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

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)

给出错误:无法实例化协议类型"拼图">

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

但是

如果我在协议中包含初始化器,那么编译器肯定知道当协议稍后被结构或类使用时,它会有一个可以使用的 init?

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

协议使我们能够编写接口,而无需多重继承的复杂性。在像C++这样的多重继承语言中,你必须处理这样一个事实,即单个类D可能从另外两个类BC继承,并且这两个类可能碰巧具有同名的方法或实例变量。如果它们都有methodA(),并且B::methodA()C::methodA()不同,那么当有人调用D的继承methodA()时,您会使用哪一个?更糟糕的是,如果BC都派生自一个公共基类A呢?协议通过不直接实例化来避免很多这种情况,同时仍然提供使多重继承具有吸引力的接口多态性。

我知道

我做不到 - 我只是想了解为什么 编译器做不到吗?

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

作为一个现实世界的例子,考虑到我刚刚说的"表"(作为一个抽象的层次(,我很确定你会理解我在说什么! 然而,我们没有提到关于它的细节(例如它的材料或它有多少条腿......在某些时候,如果我说"为我创建一个表"(实例化一个对象(,你可以问我关于规格!这就是为什么编译器不会让你直接从协议创建对象。这就是让事物变得抽象的意义所在。

另外,检查:为什么不能创建抽象类的对象?可能会有所帮助。

当你实例化一个对象时,操作系统必须知道如何在内存中分配和处理这种对象:它是一个引用类型(类(吗?强、弱或无主参考?或者它是一个值类型(结构、字符串、整数等(?

引用类型存储在堆中,而值类型存储在堆栈中。以下是对两者之间区别的彻底解释。

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

至于初始化,苹果文档是怎么说的:

初始化是准备类实例的过程, 结构或枚举以供使用。此过程涉及设置 该实例上每个存储属性的初始值并执行 在新的之前所需的任何其他设置或初始化 实例已准备就绪,可供使用。

不幸的是,即使有这样的"黑客",swift 也不允许这样做

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

最新更新