我想在init参数中使用Self,如下所示:
class A {
public init(finishBlock: ((_ operation: Self) -> Void)? = nil) {...}
}
我知道我可以在这个地方使用“A”,但我希望实现这一点,如果某个类继承自A,那么它的初始化程序会知道操作是它的类类型而不仅仅是A.所以例如如果我写道:
class B: A {
public init(finishBlock: ((_ operation: Self) -> Void)? = nil) {...}
public func fooOnlyInB() {}
}
然后我可以使用:
let b = B { (operation) in
operation.fooOnlyInB()
}
这有可能吗?
答案 0 :(得分:2)
您可以简单地覆盖每个子类,而不是在每个初始化程序中使用Self
或A
。初始化者将其自己的类型用作operation
。
这是有效的,因为A
的初始化程序声明operation
应该是符合A
的类型,当您覆盖它时,您可以自由使用子类将A
改为operation
。但是,如果您将operation
更改为不相关的类型,例如String
或Int
,则编译器不会覆盖现有的初始化程序。
首先,使用A
:
init
class A {
init(finishBlock: ((_ operation: A) -> Void)?) {...}
}
现在要创建子类,必须使用子类'覆盖init
。输入operation
代替。在致电super.init
时,强制转发operation
($0
)到您的子类'输入finishBlock
,然后使用此operation
调用class B: A {
override init(finishBlock: ((_ operation: B) -> Void)?) {
// Perform custom initialisation...
super.init { finishBlock?($0 as! B) }
}
func fooOnlyInB() {
print("foo")
}
}
。
B
B
的初始化程序现在将operation
作为init
传递,这意味着您不再需要自己投射!这要归功于您可以使用更具体的类型覆盖B
,在这种情况下为let b = B { operation in
operation.fooOnlyInB() // prints "foo"
}
。
SharedPreferences
答案 1 :(得分:1)
不,我认为这是不可能的,因为你只能在协议中使用Self
,因为它只是符合该协议的类型的占位符。它不是A
或B
这样的真实类型,因此您无法在定义类时使用它,就好像它是Any
或AnyObject
一样。
因此,让我们创建一个协议,强制符合类型实现init(finishBlock:)
初始值设定项:
protocol BlockInitializable {
init(finishBlock: ((_ operation: Self) -> Void)?)
}
class A: BlockInitializable {
init() {}
convenience required init(finishBlock: ((_ operation: A) -> Void)?) {
self.init()
finishBlock?(self)
}
}
您将收到以下错误:
协议“已阻止”要求 init(finishBlock:)无法满足非最终类
(A)
,因为它在非参数非结果中使用“Self”类型位置。
更糟糕的是,您将失去参数操作的通用类型Self
。
要解决此问题,您应该将您的类标记为final或使用 struct 这是最终类型。但是,您将失去对这些类型进行子类化的能力。为什么你必须这样做以及为什么你不能在子类型中使用Self
的原因解释here所以我建议你去看看它。
我将使用后一个选项并使用struct:
protocol Initializable {
init()
}
protocol BlockInitializable: Initializable {
init(finishBlock: ((_ operation: Self) -> Void)?)
}
extension BlockInitializable {
init(finishBlock: ((_ operation: Self) -> Void)?) {
self.init()
finishBlock?(self)
}
}
然后将A
和B
定义为struct:
struct A: BlockInitializable {
}
struct B: BlockInitializable {
func fooOnlyInB() {
print("Only in B")
}
}
您将能够执行以下操作:
let blockA = A { operation in
print("Only in A")
}
let blockB = B { operation in
operation.fooOnlyInB()
}
您可以从here下载游乐场。
答案 2 :(得分:1)
正如其他人已经说过的那样,你不能直接这样做,因为目前只在协议中可用,或者作为类中方法的返回类型。
但是,您可以通过使用协议扩展来解决此问题,以声明初始化:
Self
这不是一个完美的解决方案,因为它使protocol AProtocol : A {}
// Pre Swift 5:
// protocol AProtocol {
// init()
// }
extension AProtocol {
init(completion: ((Self) -> Void)? = nil) {
self.init()
completion?(self)
}
}
class A : AProtocol {
required init() {}
}
class B : A {
func fooOnlyInB() {
print("foo in B")
}
}
let a = A(completion: {
print($0) // A
})
let b = B(completion: {
$0.fooOnlyInB() // foo in B
})
很难添加自己的存储属性,因为它们必须被赋予默认值才能满足{ {1}}。
但实际上,据我所知,没有真正的技术理由可以解释为什么你不能使用B
作为闭包方法参数的参数。一堂课。
函数参数是逆变的,因此required init()
是Self
的子类型。因此,(A) -> Void
可以覆盖(B) -> Void
(再次应用逆变),因此使用((A) -> Void) -> T
作为传递给方法的函数的参数应该是完全合法的。
这只是该语言尚未完全支持的内容(SR-10121)。