読者です 読者をやめる 読者になる 読者になる

定食屋おろポン

おろしポン酢と青ネギはかけ放題です

Swiftのアクセスコントロールはファイル/モジュール単位だから気ィ付けや

公式ドキュメントはこちら The Swift Programming Language: Access Control

※ドキュメント以上のことは記載されていません。

SwiftにもXcode beta4からアクセスコントロールが可能になりました。 privateとかpublicとかinternalといった修飾子を付けることで、外部に公開する必要のないメソッドやクラスを隠蔽できます。

privatepublicを指定するのは、主に「クラス」と「インスタンス変数」だと思います。 「外部に公開するクラスはパブリックにしよう」とか、「この変数はクラス外から参照される必要がないので、プライベートにしよう」とかですね。

ただし、Swiftのアクセスコントロールはファイル/モジュール単位であって、クラス単位ではありません!!

各修飾子とアクセスレベルの対応は表のようになります。

アクセス修飾子 アクセス可能範囲
public モジュールの外からもアクセスできる
internal モジュールの内部ならどこからでもアクセスできる
private 同じファイル内からのみアクセスできる

なお、Swiftでいうところの「モジュール」とはザックリ言うとライブラリ(フレームワーク)です。

コードで見てみましょう。 Personクラスでは、name変数やgreeting変数はprivate宣言されています。 にも関わらず、SampleClassの中から変数もメソッドもアクセスできてしまっています。エラーもワーニングも起きません。

AccessControlSample.swift

internal class SampleClass {
    internal func sampleMethod() {
        // インスタンス化する
        let taro :Person = Person(name: "Taro")
        let mary :Person = Person(name: "Mary")

        // プライベートメソッドを呼ぶ
        taro.greeting() //=> My name is Taro
        mary.greeting() //=> My name is Mary

        // プライベートインスタンス変数にアクセスする
        println("taro.name: \(taro.name)") //=> taro.name: Taro
        println("mary.name: \(mary.name)") //=> mary.name: Mary
    }
}

private class Person {
    private let name :String

    init(name: String) {
        self.name = name
    }

    private func greeting() {
        println("My name is \(self.name)")
    }
}

これは、SampleClassPersonを同一ファイルに記述したためです。 基本的には「1クラスにつき1ファイル」を記述するので、普段はあまり問題になりませんが。

デフォルトのアクセスレベル

明示的にアクセスレベルを指定しない場合はinternalとなります。

余談

公式ドキュメントに「ターゲットがひとつしかないアプリでは、デフォルトのInternalのママで大体事足りるから、アクセスレベルを明示的に指定しなくていいよー。まぁ、モジュール内の他のコードから実装隠したいときはprivate付けておきなね。」的なことが書いてあります。

これは賛同しかねます。フレームワーク作者ではなくても、実装するクラスで「何がinternalで何がprivateなのか」はとても大事な情報だと思いますよ。

特に、Swiftではヘッダーファイルが無いため、「このクラスのAPIはどれなんだろう」というのが非常に分かりづらいです。 「ファイル外(≒クラス外)からアクセスすることを想定していない変数やメソッド」は全てprivate指定するほうがよいでしょう。

プライベート変数・メソッドはアンダースコアから始めるっていうコーディング規約が広まらないかなあ。