ProgrammingiOS/Swift開発者

Swiftにおける初期化子委譲(Initializer Delegation)とは何か、指定初期化子と便利初期化子間の委譲ルールはどのように機能し、それが継承にどのように影響するか。

Hintsage AIアシスタントで面接を突破

答え。

初期化子委譲とは、クラスのためにSwiftに組み込まれた初期化の委譲システムです。Swiftでは、指定初期化子(クラスのメイン初期化子で、すべてのプロパティの完全初期化を担当)と便利初期化子(さまざまなパラメータセットを使用してインスタンスを作成するのを補助する)との間に歴史的な違いがあります。

問題: クラスとそのサブクラス間で初期化子が無秩序に実行されると、基本クラスが完全に初期化されない、または初期化が1回以上行われる可能性があり、結果として無効なオブジェクトが生成されることがあります。

解決策は、厳格なルールです:

  1. 指定初期化子は常にスーパークラスの指定初期化子を呼び出します。
  2. 便利初期化子は常に同じクラス内の他の初期化子(指定初期化子または他の便利初期化子)を呼び出します。
  3. 便利初期化子はスーパークラスを直接初期化することはできません。

コード例:

class Vehicle { var wheels: Int // 指定初期化子 init(wheels: Int) { self.wheels = wheels } // 便利初期化子 convenience init() { self.init(wheels: 4) } } class Car: Vehicle { var color: String // 指定初期化子 init(wheels: Int, color: String) { self.color = color super.init(wheels: wheels) } // 便利初期化子 convenience init(color: String) { self.init(wheels: 4, color: color) } }

主な特徴:

  • 異なるパラメータセットでインスタンスを作成する際のコードの最小限の重複。
  • クラスの安全な継承をサポート。
  • オブジェクトのすべてのプロパティの正しい初期化が保証される。

トリックのある質問。

質問 1: 便利初期化子はスーパークラスの初期化子を直接super.initで呼び出すことができますか?

いいえ、便利初期化子は常に現在のクラスの別の初期化子に初期化を委譲し、その後、スーパークラスの指定初期化子を呼び出すことができます。

質問 2: サブクラスで必須の初期化子を実装しなかった場合、どうなりますか?

スーパークラスに必須の初期化子がある場合は、各サブクラスで必ずオーバーライドする必要があります(requiredを使用)、さもなければコンパイラがエラーを出します。

質問 3: 便利初期化子と便利必須初期化子の違いは何ですか?

便利必須初期化子は、特定の便利初期化子がスーパークラスで必須として宣言されている場合、継承階層内での初期化をサポートするために必要です。

よくあるエラーとアンチパターン

  • 便利初期化子の呼び出しのチェーンが不適切で、プロパティの初期化が破られる。
  • サブクラスで必須の初期化子を実装するのを忘れる。
  • 便利初期化子からsuper.initを呼び出すが、それは無効です。

実生活の例

ネガティブケース

開発者が便利初期化子からsuper.initを呼び出しました。コードは特定の制約の不在によりコンパイルされましたが、実行時にエラーが発生しました:オブジェクトのすべてのプロパティが初期化されていませんでした。

利点:

  • コードは他の言語の類似の実装に似ていたため、初心者にとって理解しやすく思われました。

欠点:

  • 継承時にバグが発生し、子クラスが正しく初期化されず、アプリケーションがクラッシュしました。
  • リファクタリングが必要でした。

ポジティブケース

明確に構造化された指定初期化子と便利初期化子を使用し、相互に明示的に呼び出しました。ロジックはSwiftのルールに厳密に従って呼び出されたため、初期化は常に透明で正確でした。

利点:

  • クラス階層を簡単に拡張および保守できる。
  • 重複するコードが排除される。
  • テストが簡素化される。

欠点:

  • 大きな階層内の初期化子の継承について注意深く文書化する必要がありました。