2つのクラスを検討してください。
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
super.init() // Error: Must call a designated initializer of the superclass 'A'
}
}
これが許可されない理由はわかりません。最終的に、各クラスの指定されたイニシャライザは必要な値で呼び出されるので、なぜB
のデフォルト値を再度指定してinit
のx
で繰り返す必要があるのかinit
の便利なA
はうまくいくでしょうか?
これは、「Swiftプログラミングガイド」で指定されている「イニシャライザーチェーン」ルールのルール1です。
ルール1:Designated初期化子は、直接のスーパークラスからdesignated初期化子を呼び出す必要があります。
強調鉱山。指定された初期化子は、便利な初期化子を呼び出すことはできません。
どのイニシャライザの「方向」が許可されるかを示すための規則に沿った図があります。
検討する
class A
{
var a: Int
var b: Int
init (a: Int, b: Int) {
print("Entering A.init(a,b)")
self.a = a; self.b = b
}
convenience init(a: Int) {
print("Entering A.init(a)")
self.init(a: a, b: 0)
}
convenience init() {
print("Entering A.init()")
self.init(a:0)
}
}
class B : A
{
var c: Int
override init(a: Int, b: Int)
{
print("Entering B.init(a,b)")
self.c = 0; super.init(a: a, b: b)
}
}
var b = B()
クラスAの指定されたすべての初期化子がオーバーライドされるため、クラスBはAのすべての便利な初期化子を継承します。
Entering A.init()
Entering A.init(a:)
Entering B.init(a:,b:)
Entering A.init(a:,b:)
ここで、指定されたイニシャライザーB.init(a:b :)が基本クラスの便利なイニシャライザーA.init(a :)の呼び出しを許可された場合、これはB.init(a:、b:の再帰呼び出しになります。 )。
それは、無限の再帰で終わる可能性があるからです。考慮してください:
_class SuperClass {
init() {
}
convenience init(value: Int) {
// calls init() of the current class
// so init() for SubClass if the instance
// is a SubClass
self.init()
}
}
class SubClass : SuperClass {
override init() {
super.init(value: 10)
}
}
_
そして見て:
_let a = SubClass()
_
SubClass.init()
を呼び出すSuperClass.init(value:)
を呼び出すSubClass.init()
を呼び出します。
指定/便利な初期化ルールは、クラスの初期化が常に正しいように設計されています。
このための回避策を見つけました。非常にきれいではありませんが、スーパークラスの値を知らない、またはデフォルト値を設定したくないという問題を解決します。
あなたがしなければならないのは、サブクラスのinit
の中にある便利なinit
を使用して、スーパークラスのインスタンスを作成することだけです。次に、作成したインスタンスを使用して、スーパーの指定されたinit
を呼び出します。
class A {
var x: Int
init(x: Int) {
self.x = x
}
convenience init() {
self.init(x: 0)
}
}
class B: A {
init() {
// calls A's convenience init, gets instance of A with default x value
let intermediate = A()
super.init(x: intermediate.x)
}
}
便利なinit()
から新しいヘルパー関数foo()
に初期化コードを抽出し、foo(...)
を呼び出してサブクラスで初期化を行うことを検討してください。
初期化子とその継承の詳細な説明については、18:30のWWDCビデオ「403 Intermediate Swift」を参照してください。私が理解したように、次のことを考慮してください。
class Dragon {
var legs: Int
var isFlying: Bool
init(legs: Int, isFlying: Bool) {
self.legs = legs
self.isFlying = isFlying
}
convenience initWyvern() {
self.init(legs: 2, isFlying: true)
}
}
しかし、今度はWyrmサブクラスを考えてみましょう。Wyrmは、脚も翼もないドラゴンです。したがって、ワイバーンのイニシャライザー(2脚、2羽)は間違っています!便利なWyvern-Initializerを単に呼び出すことはできず、指定された完全なInitializerのみを呼び出すことができる場合、このエラーは回避できます。
class Wyrm: Dragon {
init() {
super.init(legs: 0, isFlying: false)
}
}