既存の答えはconvenience
物語の半分しか伝えません。ストーリーの残りの半分、既存の回答のいずれもカバーしていない半分は、Desmondがコメントで投稿した質問に回答します。
Swiftがconvenience
イニシャライザから呼び出す必要があるからといって、なぜイニシャライザの前に置くよう強制self.init
するのですか? `
この回答では少し触れましたが、Swiftの初期化ルールのいくつかを詳細にカバーしていますが、主な焦点はこのrequired
単語にありました。しかし、その答えは、この質問とこの答えに関連する何かにまだ対処していました。Swift初期化子の継承がどのように機能するかを理解する必要があります。
Swiftは初期化されていない変数を許可しないため、継承元のクラスからすべての(または任意の)初期化子を継承することは保証されていません。サブクラスを作成し、初期化されていないインスタンス変数をサブクラスに追加すると、初期化子の継承が停止します。そして、独自のイニシャライザを追加するまで、コンパイラは私たちに怒鳴りつけます。
明確にするために、初期化されていないインスタンス変数は、デフォルト値が指定されていないインスタンス変数です(オプションと暗黙的にアンラップされたオプションは、自動的にデフォルト値と見なされることに注意してくださいnil
)。
したがって、この場合:
class Foo {
var a: Int
}
a
初期化されていないインスタンス変数です。a
デフォルト値を指定しない限り、これはコンパイルされません。
class Foo {
var a: Int = 0
}
またはa
、初期化メソッドで初期化します。
class Foo {
var a: Int
init(a: Int) {
self.a = a
}
}
では、サブクラス化するFoo
とどうなるか見てみましょう。
class Bar: Foo {
var b: Int
init(a: Int, b: Int) {
self.b = b
super.init(a: a)
}
}
正しい?変数を追加し、値を設定するための初期化子を追加してb
コンパイルします。使用している言語によっては、がイニシャライザBar
を継承していることが予想されます。しかし、そうではありません。そして、どうすればよいでしょうか?どのようにないのは、に値を代入する方法を知っているという変数を追加しますか?そうではありません。したがって、すべての値を初期化できないイニシャライザでインスタンスを初期化することはできません。Foo
init(a: Int)
Foo
init(a: Int)
b
Bar
Bar
これは何と関係がありconvenience
ますか?
さて、初期化子の継承に関するルールを見てみましょう:
ルール1
サブクラスが指定された初期化子を定義していない場合、サブクラスは指定された初期化子のすべてのスーパークラスを自動的に継承します。
ルール2
サブクラスがすべてのスーパークラス指定イニシャライザーの実装を提供する場合(ルール1に従って継承するか、カスタム定義をその定義の一部として提供することにより)、スーパークラスの便利な初期化子すべてを自動的に継承します。
便利なイニシャライザに言及するルール2に注意してください。
したがって、convenience
キーワードが行うことは、デフォルト値なしでインスタンス変数を追加するサブクラスが継承できる初期化子を示すことです。
この例のBase
クラスを見てみましょう:
class Base {
let a: Int
let b: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
}
convenience init() {
self.init(a: 0, b: 0)
}
convenience init(a: Int) {
self.init(a: a, b: 0)
}
convenience init(b: Int) {
self.init(a: 0, b: b)
}
}
convenience
ここには3つのイニシャライザがあることに注意してください。つまり、継承可能な3つのイニシャライザがあるということです。そして、指定されたイニシャライザが1つあります(指定されたイニシャライザは、便利なイニシャライザではない任意のイニシャライザです)。
基本クラスのインスタンスを4つの異なる方法でインスタンス化できます。
それでは、サブクラスを作成しましょう。
class NonInheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
}
から継承していBase
ます。独自のインスタンス変数を追加し、デフォルト値を指定しなかったため、独自の初期化子を追加する必要があります。1つ追加しましたinit(a: Int, b: Int, c: Int)
が、Base
クラスの指定された初期化子の署名と一致しません:init(a: Int, b: Int)
。ことは、私たちが継承していない任意の初期化子をからBase
:
私たちはから継承されたのであれば、何が起こるだろうBase
が、我々は先に行って、からの指定イニシャライザと一致したことをイニシャライザを実装しましたかBase
?
class Inheritor: Base {
let c: Int
init(a: Int, b: Int, c: Int) {
self.c = c
super.init(a: a, b: b)
}
convenience override init(a: Int, b: Int) {
self.init(a: a, b: b, c: 0)
}
}
ここで、このクラスに直接実装した2つの初期化子に加えて、Base
クラスの指定された初期化子に一致する初期化子を実装したため、Base
クラスのすべてのconvenience
初期化子を継承します。
一致するシグネチャを持つイニシャライザがマークされconvenience
ているという事実は、ここでは違いはありません。これは、Inheritor
指定された初期化子が1つしかないことを意味します。したがって、から継承する場合Inheritor
、指定された1つの初期化子を実装する必要があります。次に、Inheritor
の便利な初期化子Base
を継承しconvenience
ます。つまり、すべてのの指定された初期化子を実装し、その初期化子を継承できます。