Swift 3からのアクセスコントロール


Swift 3でクラスやメソッドにつける publicprivate などのアクセスコントロールが見直されました。新しいキーワードが増えただけでなく、これまでのキーワードの意味も変わったので、何がどう変わったのか、それらをどう使い分けていくのかというところを自分なりにまとめました。

これまでのアクセスコントロール

Swift 2までは次の3つのアクセスレベルがありました。何もつけなければ internal が指定されたことになります。

  • public: 同じモジュール内だけでなく、別のモジュールからでもアクセスできる
  • internal: 同じモジュール内からのみアクセスできる
  • private: 同じソースファイル内からのみアクセスできる

Swift 3からのアクセスコントロール

Swift 3からは次の5つのアクセスレベルになります。何も付けなければ internal になる点はこれまでと同じです。

  • open: 同じモジュール内だけでなく、別のモジュールからでもアクセスでき、別モジュールで継承またはオーバーライドができる(Swift 2までの public と同じ振る舞い)
  • public: 同じモジュール内だけでなく、別のモジュールからでもアクセスできるが、別モジュールでは継承、オーバーライドはできない
  • internal: 同じモジュール内からのみアクセスできる
  • fileprivate: 同じソースファイル内からのみアクセスできる(Swift 2までの private と同じ振る舞い)
  • private: 定義されたスコープ内でのみアクセスできる

キーワードとしては openfileprivate が増えたわけですが、これらはそれぞれSwift 2までの publicprivate に相当する点に注意です。むしろ、Swift 3では publicprivate の意味が変わったと考えた方がいいでしょう。

新しい public がSwift 2までの public と異なるのは、別モジュールでの継承・オーバーライドを認めない点です。クラスに public を付けると、別のモジュールからでもそのクラスを使うことはできますが、そのクラスを継承することはできません。同じように、 public を付けたメソッドは、別のモジュールからでも呼び出すことができますが、継承したクラスでそのメソッドをオーバーライドすることができません。

新しい private はSwift 2までの private と異なり、それが定義されたスコープ内にしかアクセスを許可しなくなりました。こちらはJavaやC#の private に近づいた感がありますね。

どう使い分けるのか

さて、Swift 3からの5つのアクセスレベルをどのように使い分けたらいいでしょうか。
そのヒントは今回の変更点のプロポーザルにあるように思います。

SE-0025の方には、従来の private では、完全に隠したいのか、それとも関連するコード間でのみ共有したいのかが明確でなかったということが書かれています。
SE-0117の方では、サブクラス化やオーバーライドを許すことでライブラリ設計上の問題を起こしやすいことや、オーバーライドを許さなければコンパイラのコード生成における最適化がもっと期待できるというようなことが書かれています。

これらを踏まえると、私は次のように使い分けるのがいいと思いました。

  • アクセスレベルを特に意識しないプログラム → 何もつけない or internal
  • アクセスレベルを意識する場合
    • 別のモジュールからも使えるようにしたい → public
      • そのうち、継承させたりオーバーライドさせることが前提のもの → open
    • 実装を他からは隠したい → private
      • でも特定のコード間では共有したいとき → fileprivate にして共有したいコードを同じソースファイルにまとめる(うまくextensionを使うのが良さそうですね)
    • それ以外 → 何もつけない or internal

つまり、普段は public, internal, private の3つを使うようにして、特定の意図があるときにのみ openfileprivate を使うというのがいいんじゃないでしょうか。
新しいキーワードを増やすだけでなく、既存のキーワードの意味を変えてきたことからも、普段は publicprivate を使えということだろうと思います。

既存コードをXcode 8のコードマイグレーションでSwift 3にマイグレートすると、従来の publicopen に、 privatefileprivate に書き換えられてしまいます。しかし、ここは注意して publicprivate に戻していきたいところです。