[Swift 4] privateとfileprivateの挙動が変わりますよというお話
はじめに
モバイルアプリサービス部の中安です。
Swift3でスコープの改変が行われ、「private」に加えて、「fileprivate」というスコープが加わりました。 Swift4ではさらにスコープの改変が行われるそうで、fileprivate の役割がまた少し変わってきます。
自分も忘れてしまわぬよう、しっかりと予習復習をしておこうとブログにしたためることにしました。
Swift3での挙動
fileprivate は、簡単にいうと「同一ファイル内であればアクセスできる」というスコープとして登場しました。 ファイルひとつをモジュールとして考えたときに、その内部では隠蔽されなくてよいだろうという考え方なんだろうと思います。 つまりは、別のクラスや構造体などであっても、その同一ファイル内であれば見ることができるということですね。
具体的にソースで見てみます。
class Hoge { private var privateText = "" fileprivate var fileprivateText = "" func doSomethingInDeclaration() { print(privateText) print(fileprivateText) } } // Hogeの拡張 extension Hoge { func doSomethingInExtension() { print(privateText) print(fileprivateText) } } // Hogeの継承クラス class HogeChild: Hoge { func doSomethingInInheritance() { print(privateText) print(fileprivateText) } } // Hogeとは無関係のクラス class Fuga { func doSomethingInOtherClass() { let hoge = Hoge() print(hoge.privateText) print(hoge.fileprivateText) } }
Hoge
というクラスを用意し、private なプロパティと、 fileprivate なプロパティを用意しました。
そして、同一ファイル内に Hoge
に関わる3つの異なる定義をしています。
Hoge
のextensionHoge
の継承クラスHoge
とは関係のない別クラス
これらの各定義から Hoge
のストアドなプロパティ値にアクセスしようとしてるのが分かると思います。
こういった場合にはどこでスコープエラーが起きるでしょうか。結果は下記のようになります。
private | fileprivate | |
---|---|---|
Hoge | ◯ | ◯ |
Hoge(extension) | ✕ | ◯ |
HogeChild | ✕ | ◯ |
Fuga | ✕ | ◯ |
上表のように private は Hoge
自身以外すべてアクセス不可でエラーになります。
すなわち、Hoge
の privateなプロパティは Hoge
の定義内でのみしかアクセスできません。
しかし、fileprivateは軒並みすべてアクセスすることができます。
Hoge
とは無関係である Fuga
クラスにおいても、Hoge
の中身を覗けることになります。
Swift4での挙動
では、Swift4ではどのように変更されたでしょうか。 先程の例と見比べてやってみます。
private | fileprivate | |
---|---|---|
Hoge | ◯ | ◯ |
Hoge(extension) | ◯ | ◯ |
HogeChild | ✕ | ◯ |
Fuga | ✕ | ◯ |
異なる箇所は、Hoge
の extension で privateプロパティにアクセスできるようになっているというところです。
Swift4では何故このような変更が行われたのでしょうか。その詳しい内容は、こちらの Improve Interaction Between private Declarations and Extensions ほうに載ってはいますが、 「拡張を多く用いるのがSwiftのはずなのに、これじゃ fileprivate ばかりになっちゃうよ」というところなのでしょうか。たしかにSwift2からSwift3になったとき、自動コンバートをすると軒並み fileprivate に切り替えられた記憶があります。
この変更により、Swift4では同一ファイル内の extension は下記のようにできます。
class Hoge { private var data = [String : Any]() init(data: [String : Any]) { self.data = data } } extension Hoge { func value(of key: String) -> Any? { return data[key] // ← Swift3だとエラーだが、Swift4ならOK } } let hoge = Hoge(data: ["name" : "勇者", "hp" : 20]) print(hoge.value(of: "hp")) print(hoge.value(of: "exp"))
これをSwift3では fileprivate var data = [String : Any]()
と宣言してやらなくてはなりませんでした。
この変更で、private の意味からしても分かりやすくなったように思えます。
以上です。