为什么当我在 swift 中扩展 ExpressibleByArrayLiteral 协议时,我需要使 init 成为必需的。在协议的定义中,init方法只是公共的。
我的内容与文档中的内容几乎相同,https://developer.apple.com/reference/swift/expressiblebyarrayliteral,而且编译器仍然抱怨将其设置为必需的
init(arrayLiteral: Element...)
。我唯一的区别是我在一个类中实现它,而不是在一个结构中。有什么建议吗?
更新:
这是我的代码的实现:
public class Stack<T> {
private var buffer: [T]
init() {
self.buffer = []
}
public func push(_ value: T) {
self.buffer.append(value)
}
public func pop() -> T? {
return self.buffer.popLast()
}
var size: Int {
return self.buffer.count
}
var isEmpty: Bool {
return self.size == 0
}
}
extension Stack: ExpressibleByArrayLiteral {
init(arrayLiteral: T...) {
for item in arrayLiteral {
self.push(item)
}
}
}
我遇到的错误是:
指定的初始值设定项不能在“Stack”的扩展中声明;您的意思是这是一个方便的初始化程序吗?
初始化程序“init(arrayLiteral:)”必须声明为公共,因为它符合公共协议“ExpressibleByArrayLiteral”中的要求
初始化器要求 'init(arrayLiteral:)' 只能通过非最终类 'Stack' 定义中的
初始化器来满足required
问题:
错误 #1 -> 为什么我不能在扩展中声明指定的 init?
错误#2 ->我理解这部分,协议将其定义为公共。但为什么文档在没有 public 关键字的情况下实现它?
错误#3 ->这几乎是我最大的问题,为什么需要这样做。
原因是继承:协议内的任何
init
如果被类采用,则必须标记为 required
。 struct
不能被继承。
对于类,
init
必须初始化同一类的实例或返回nil
。当该类采用协议时,它本身及其所有子类必须提供自己的实现,以便编译器可以验证这一事实:
class ClassA : ExpressibleByArrayLiteral {
required init(arrayLiteral elements: Self.Element...) {
// this implicitly returns an instance of ClassA
}
}
class ClassB : ClassA {
// without its own init(arrayLitteral:), it will fallback on ClassA's init and return
// an instance of ClassA, which Swift does not allow.
}
这是由于 Swift 的静态类型系统造成的。与 Objective-C 不同,在 Objective-C 中,您认为创建了一个
NSString
,但实际上却得到了一个 _NSContiguousString
(所谓的 类簇 设计模式)
NSString * str = [[NSString alloc] initWithString:@"Hello world"];
// surprise: you may get an _NSContiguousString instead
只需删除扩展并在类的定义中实现它即可:
public class Stack<T> : ExpressibleByArrayLiteral {
private var buffer: [T]
init() {
self.buffer = []
}
public func push(_ value: T) {
self.buffer.append(value)
}
public func pop() -> T? {
return self.buffer.popLast()
}
var size: Int {
return self.buffer.count
}
var isEmpty: Bool {
return self.size == 0
}
// MARK: -
required public init(arrayLiteral: T...) {
self.buffer = []
for item in arrayLiteral {
self.push(item)
}
}
}
对于错误 #1 -> 为什么我不能在扩展中声明指定的 init?
因为方便初始化器必须调用指定的初始化器。扩展名可以在其他文件中定义。使用当前的结构,该类可能没有在此编译单元(文件)中定义指定的初始化程序,这会给当前编译器带来太多混乱。 (这并不是说这是一个无法解决的类型论问题;它只是在需要弄清楚单个文件中的所有内容时会产生问题。)扩展是扩展。如果它们从根本上改变了对象的工作方式(例如创建指定的初始值设定项),那么它们可能无法成为扩展。
对于错误 #2 -> 我理解这部分,协议将其定义为公共。但为什么文档在没有 public 关键字的情况下实现它呢?
因为文档渲染器总是删除协议上的
public
。如果这导致混乱,请向 bugs.swift.org 提交错误。
对于错误 #3 -> 这几乎是我最大的问题,为什么需要这样做。
因为这不是最后一堂课。考虑是否存在
Stack
的子类,它有自己所需的初始值设定项。您如何确保 init(arrayLiteral:)
调用了它?它无法调用它(因为它不知道它存在)。因此,要么 init(arrayLiteral:)
必须是 required
(这意味着它需要成为主声明的一部分,而不是扩展),或者 Stack
必须是 final
。
如果您标记它
final
,这将按您的预期工作。如果你希望它被子类化,只需将它从扩展中移出到主体中,然后像这样定义它:
public convenience required init(arrayLiteral: T...) {
self.init()
for item in arrayLiteral {
self.push(item)
}
}