# アクセスコントロールとは

アクセスコントロールとは、モジュール内の要素に対して外部からのアクセスを制御する仕組みのことです。アクセスコントロールを使用すると、コードの実装の詳細を隠したり、使用できるインターフェイスを指定することができます。

クラス、構造体、列挙型などの型に属するプロパティ、メソッド、イニシャライザ、添え字に特定のアクセスレベルを割り当てることができます。

# モジュール

Swiftのアクセスコントロールはモジュールの概念に基づいています。

モジュールとは、コードを配布する際の単一のユニットです。フレームワークやアプリケーションは単一のユニットとしてビルドされて提供されます。提供されたモジュールは、import キーワードを使って別のモジュールからインポートすることができます。

例えば、Foundationなどのフレームワークをインポートする際は次のように書きます。

import Foundation

インポートすると、インポートしたファイル内ではFoundationが公開している型や変数などを使うことができます。非公開にしている変数や関数などにはアクセスすることはできません。この公開するか非公開にするかの範囲は アクセスレベル によって変わります。

TIP

Xcodeで、command + control を押下しながら、モジュールをクリックすると公開されているインターフェースを閲覧することができます。

試しに、Foundationフレームワークをクリックすると以下のようにインターフェースの一覧を見ることができます。

...
import Foundation.NSUndoManager
import Foundation.NSUnit
import Foundation.NSUserActivity
import Foundation.NSValue
import Foundation.NSValueTransformer
import Foundation.NSXMLParser
import Foundation.NSXPCConnection
import Foundation.NSZone
import Foundation
import Foundation.NSURLCredential
import Foundation.NSURLCredentialStorage
import Foundation.NSUserDefaults
import ObjectiveC
import _SwiftCoreFoundationOverlayShims
import _SwiftFoundationOverlayShims

/**
 `Calendar` encapsulates information about systems of reckoning time in which the beginning, length, and divisions of a year are defined. It provides information about the calendar and support for calendrical computations such as determining the range of a given calendrical unit and adding units to a given absolute time.
*/
public struct Calendar : Hashable, Equatable, ReferenceConvertible {

    public typealias ReferenceType = NSCalendar

    /// Calendar supports many different kinds of calendars. Each is identified by an identifier here.
    public enum Identifier {

        /// The common calendar in Europe, the Western Hemisphere, and elsewhere.
        case gregorian

        case buddhist

# アクセスレベル

Swiftは5つのアクセスレベルを提供しています。それぞれのアクセスレベルを設定することで、外部からのアクセスを制御することができます。

# open

open は、モジュール内外のすべてのアクセスを許可します。openはクラスとクラスメンバにのみ適用され、モジュール外でサブクラス化やオーバーライドすることができます。

// モジュール内外のすべてのアクセスを許可する
open class OpenPerson {}

# public

public は、モジュール内外のすべてのアクセスを許可します。openと異なり、モジュール外でサブクラス化やオーバーライドすることはできません。

// モジュール内外のすべてのアクセスを許可するが、継承やオーバーライドはできない。
public class PublicPerson {}

# internal

internal は、同一のモジュール内のみアクセスを許可します。モジュールの外からはアクセスすることはできません。アクセスレベルを明示的に書かないときは、デフォルトでinternalが設定されます。

public class PublicPerson {
  // 同一のモジュール内のみアクセスを許可する
  internal var internalProp: String
}

# file-private

file-private は、同一のソースファイルのみアクセスを許可します。ソースファイル外からアクセスすることはできません。

public class PublicPerson {
  // 同一のソースファイルのみアクセスを許可
  fileprivate var fileprivateProp: String
}

# private

private は、定義されたスコープ内のアクセスのみを許可します。

public class PublicPerson {
  // PublicPerson クラスのスコープ内だけアクセスを許可する
  private func privateFunc() {}
}

例えば、上記のプライベートメソッドに直接アクセスするとコンパイラはエラーを出力します。

public class PublicPerson {
  private func privateFunc() {
    print("private")
  }
}

let person = PublicPerson()

person.privateFunc()

/* 実行結果 */
// 'privateFunc' is inaccessible due to 'private' protection level

定義されたスコープ内のみアクセスできるので、PublicPerson内だったらアクセスすることができます。

public class PublicPerson {
  private func privateFunc() {
    print("private")
  }

  // 同じスコープ内なのでアクセスできる
  func callPrivate() {
    privateFunc()
  }
}

let person = PublicPerson()

person.callPrivate()

/* 実行結果 */
// private

# タプル型

タプル型のアクセスレベルは、構成されている要素の中で一番アクセスレベルの厳しいものがタプル自身のアクセスレベルになります。

次の例では、privateとpublicの二つの定数を要素として構成しています。この場合は private がタプルのアクセスレベルとして設定されます。

private let private1 = 1
public let public1 = 2

// アクセスレベルはprivateになる
let tuple = (private1, public1)

# 関数

関数のアクセルレベルは、関数の引数や戻り値の中で一番厳しいアクセスレベルが関数自身のアクセスレベルになります。

次の例では、privateレベルとpublicレベルの構造体を構成するタプルを返す関数を定義しています。この場合タプルはprivateレベルになるので関数自身のアクセスレベルは private になります。

private struct PrivateStruct {}
public struct PublicStruct {}

func method() -> (PrivateStruct, PublicStruct) {
  let privateStruct = PrivateStruct()
  let publicStruct = PublicStruct()
  
  return (privateStruct, publicStruct)
}

しかし、このままではコンパイラはエラーを出力します。関数自身にアクセスレベルを明示的に指定する必要があるためです。

// error: function must be declared private or fileprivate because its result uses a private type
// func method() -> (PrivateStruct, PublicStruct) {
//     ^            ~~~~~~~~~~~~~

// note: type declared here
// private struct PrivateStruct {}

関数に private キーワードを記述して明示的にアクセスレベルを定義します。

private struct PrivateStruct {}
public struct PublicStruct {}

// privateを記述する
private func method() -> (PrivateStruct, PublicStruct) {
  let privateStruct = PrivateStruct()
  let publicStruct = PublicStruct()
  
  return (privateStruct, publicStruct)
}