初始参数中的自我

时间:2016-10-15 06:39:55

标签: swift types initialization

我想在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()
}

这有可能吗?

3 个答案:

答案 0 :(得分:2)

您可以简单地覆盖每个子类,而不是在每个初始化程序中使用SelfA。初始化者将其自己的类型用作operation

这是有效的,因为A的初始化程序声明operation应该是符合A的类型,当您覆盖它时,您可以自由使用子类将A改为operation。但是,如果您将operation更改为不相关的类型,例如StringInt,则编译器不会覆盖现有的初始化程序。

首先,使用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,因为它只是符合该协议的类型的占位符。它不是AB这样的真实类型,因此您无法在定义类时使用它,就好像它是AnyAnyObject一样。

因此,让我们创建一个协议,强制符合类型实现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)
  }
}

然后将AB定义为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)。