Swift アクセス制御

アクセス制御は、他のソースファイルやモジュール内のコードがあなたのコードにアクセスするレベルを制限できます。

特定の型(クラス、構造体、列挙型)にアクセスレベルを明示的に設定できるだけでなく、それらの型のプロパティ、関数、初期化メソッド、基本型、サブスクリプトなどにもアクセスレベルを設定できます。

プロトコルも特定の範囲内での使用に限定できます。プロトコル内のグローバル定数、変数、関数も含みます。

アクセス制御はモジュールとソースファイルに基づきます。

モジュールとは、独立したユニットとして構築・公開される Framework または Application のことです。Swift のモジュールは、import キーワードを使用して別のモジュールを導入できます。

ソースファイルは単一のソースコードファイルであり、通常は1つのモジュールに属します。ソースファイルには複数のクラスや関数の定義を含めることができます。

Swift はコード内のエンティティに対して、4つの異なるアクセスレベルを提供します:public、internal、fileprivate、private。

アクセスレベル 定義
public 自分のモジュール内のソースファイルにある任意のエンティティにアクセスでき、他のモジュールからもそのモジュールを import することでソースファイル内のすべてのエンティティにアクセスできます。
internal 自分のモジュール内のソースファイルにある任意のエンティティにはアクセスできますが、他のモジュールからはそのモジュールのソースファイル内のエンティティにアクセスできません。
fileprivate ファイル内プライベート(fileprivate)。現在のソースファイル内でのみ使用できます。
private クラス内でのみアクセス可能です。そのクラスや構造体のスコープの外ではアクセスできません。

public は最も高いアクセスレベルで、private は最も低いアクセスレベルです。

構文

修飾子 public、internal、fileprivate、private を使用してエンティティのアクセスレベルを宣言します:

例

public class SomePublicClass {} internal class SomeInternalClass {} fileprivate class SomeFilePrivateClass {} private class SomePrivateClass {} public var somePublicVariable = 0 internal let someInternalConstant = 0 fileprivate func someFilePrivateFunction() {} private func somePrivateFunction() {}

特別な指定がない限り、エンティティはデフォルトのアクセスレベル internal を使用します。

アクセスレベルを指定しない場合、デフォルトは internal です

class SomeInternalClass {} // アクセスレベルは internal let someInternalConstant = 0 // アクセスレベルは internal

関数型のアクセス権限

関数のアクセスレベルは、その関数のパラメータ型と戻り値型のアクセスレベルに基づいて決まります。

次の例では、someFunction という名前のグローバル関数を定義していますが、そのアクセスレベルは明示的に宣言されていません。

func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

関数内のクラス SomeInternalClass のアクセスレベルは internal、もう1つの SomePrivateClass のアクセスレベルは private です。したがって、タプルのアクセスレベルに関する原則に基づき、そのタプルのアクセスレベルは private になります(タプルのアクセスレベルは、タプル内で最も低いアクセスレベルの型と一致します)。

関数の戻り値型のアクセスレベルが private であるため、private 修飾子を使用して関数を明示的に宣言する必要があります:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

この関数を public または internal として宣言するか、デフォルトのアクセスレベル internal を使用するのは誤りです。その場合、private レベルの戻り値にアクセスできないからです。


列挙型のアクセス権限

列挙型のメンバーのアクセスレベルは、その列挙型から継承されます。列挙型のメンバーに個別に異なるアクセスレベルを宣言することはできません。

例

たとえば、次の例では、列挙型 Student が明示的に public レベルで宣言されているため、そのメンバー Name、Mark のアクセスレベルも同様に public になります:

例

public enum Student { case Name(String) case Mark(Int,Int,Int) } var studDetails = Student.Name("Swift") var studMarks = Student.Mark(98,97,95) switch studMarks { case .Name(let studName): print("学生名:\(studName).") case .Mark(let Mark1, let Mark2, let Mark3): print("学生の成績:\(Mark1),\(Mark2),\(Mark3)") }

上記プログラムの実行出力結果は次のとおりです:

学生成绩: 98,97,95

サブクラスのアクセス権限

サブクラスのアクセスレベルは、スーパークラスのアクセスレベルより高くすることはできません。たとえば、スーパークラスのアクセスレベルが internal の場合、サブクラスのアクセスレベルを public と宣言することはできません。

例

public class SuperClass { fileprivate func show() { print("スーパークラス") } } // 访问级别不能高于超类 public > internal internal class SubClass: SuperClass { override internal func show() { print("サブクラス") } } let sup = SuperClass() sup.show() let sub = SubClass() sub.show()

上記プログラムの実行出力結果は次のとおりです:

スーパークラス
サブクラス

定数、変数、プロパティ、サブスクリプトのアクセス権限

定数、変数、プロパティは、その型よりも高いアクセスレベルを持つことはできません。

たとえば、public レベルのプロパティを定義しても、その型が private レベルの場合、コンパイラはこれを許可しません。

同様に、サブスクリプトも、インデックス型や戻り値型よりも高いアクセスレベルを持つことはできません。

定数、変数、プロパティ、サブスクリプトの定義型が private レベルの場合、それらは明示的にアクセスレベルを private と宣言する必要があります:

private var privateInstance = SomePrivateClass()

Getter と Setter のアクセス権限

定数、変数、プロパティ、サブスクリプトの Getter と Setter のアクセスレベルは、それらが属するメンバーのアクセスレベルを継承します。

Setter のアクセスレベルは、対応する Getter のアクセスレベルより低くすることができます。これにより、変数、プロパティ、またはサブスクリプトの読み取り・書き込み権限を制御できます。

例

class Samplepgm { fileprivate var counter: Int = 0{ willSet(newTotal){ print("カウンタ:\(newTotal)") } didSet{ if counter > oldValue { print("追加数量\(counter - oldValue)") } } } } let NewCounter = Samplepgm() NewCounter.counter = 100 NewCounter.counter = 800

counter のアクセスレベルは fileprivate であり、ファイル内でアクセスできます。

上記プログラムの実行出力結果は次のとおりです:

计数器: 100
新增加数量 100
计数器: 800
新增加数量 700

イニシャライザとデフォルトイニシャライザのアクセス権限

初期化

カスタムイニシャライザにはアクセスレベルを宣言できますが、それが属するクラスのアクセスレベルより高くすることはできません。ただし、必須イニシャライザは例外で、そのアクセスレベルは所属するクラスのアクセスレベルと同じでなければなりません。

関数やメソッドのパラメータと同様に、イニシャライザのパラメータのアクセスレベルも、イニシャライザのアクセスレベルより低くすることはできません。

デフォルトイニシャライザ

Swift は構造体とクラスに、すべてのプロパティに値を割り当てるためのデフォルトの引数なしイニシャライザを提供しますが、具体的な値は指定しません。

デフォルトイニシャライザのアクセスレベルは、所属する型のアクセスレベルと同じです。

例

各サブクラスの init() メソッドの前に required キーワードを使用してアクセス権限を宣言します。

例

class classA { required init() { var a = 10 print(a) } } class classB: classA { required init() { var b = 30 print(b) } } let res = classA() let show = classB()

上記プログラムの実行出力結果は次のとおりです:

10
30
10

プロトコルのアクセス権限

プロトコルにアクセスレベルを明示的に宣言する場合は、宣言したアクセスレベルのスコープ内でのみそのプロトコルを使用するようにする必要があることに注意してください。

public アクセスレベルのプロトコルを定義した場合、そのプロトコルを実装するために提供される必要な関数も public のアクセスレベルになります。これは他の型とは異なります。たとえば、public アクセスレベルの他の型では、そのメンバーのアクセスレベルは internal です。

例

public protocol TcpProtocol { init(no1: Int) } public class MainClass { var no1: Int // local storage init(no1: Int) { self.no1 = no1 // initialization } } class SubClass: MainClass, TcpProtocol { var no2: Int init(no1: Int, no2 : Int) { self.no2 = no2 super.init(no1:no1) } // Requires only one parameter for convenient method required override convenience init(no1: Int) { self.init(no1:no1, no2:0) } } let res = MainClass(no1: 20) let show = SubClass(no1: 30, no2: 50) print("res is: \(res.no1)") print("res is: \(show.no1)") print("res is: \(show.no2)")

上記プログラムの実行出力結果は次のとおりです:

res is: 20
res is: 30
res is: 50

拡張のアクセス権限

条件が許せば、クラス、構造体、列挙型を拡張できます。拡張メンバーは、元のクラスのメンバーと同じアクセスレベルを持つ必要があります。たとえば、パブリック型を拡張する場合、新しく追加するメンバーは元のメンバーと同じデフォルトの internal アクセスレベルを持つ必要があります。

あるいは、拡張のアクセスレベルを明示的に宣言して(たとえば private extension を使用)、拡張内のすべてのメンバーに新しいデフォルトのアクセスレベルを設定できます。この新しいデフォルトのアクセスレベルは、個々のメンバーが宣言するアクセスレベルによって上書きされることもあります。


ジェネリックのアクセス権限

ジェネリック型またはジェネリック関数のアクセスレベルは、ジェネリック型、関数自体、ジェネリック型パラメータのうち、最も低いアクセスレベルになります。

例

public struct TOS<T> { var items = [T]() private mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } } var tos = TOS<String>() tos.push("Swift") print(tos.items) tos.push("ジェネリック") print(tos.items) tos.push("型パラメータ") print(tos.items) tos.push("型パラメータ名") print(tos.items) let deletetos = tos.pop()

上記プログラムの実行出力結果は次のとおりです:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]

型エイリアス

定義した型エイリアスは、アクセス制御の目的で異なる型として扱われます。型エイリアスのアクセスレベルは、元の型のアクセスレベルより高くすることはできません。

たとえば、private レベルの型エイリアスは、public、internal、private のいずれの型にも設定できます。しかし、public レベルの型エイリアスは public レベルの型にのみ設定でき、internal または private レベルの型には設定できません。

注意:この規則は、プロトコル準拠を満たすために関連する型にエイリアスを付ける場合にも適用されます。

例

public protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } struct Stack<T>: Container { // original Stack<T> implementation var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } // conformance to the Container protocol mutating func append(item: T) { self.push(item) } var count: Int { return items.count } subscript(i: Int) -> T { return items[i] } } func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, anotherContainer: C2) -> Bool { // check that both containers contain the same number of items if someContainer.count != anotherContainer.count { return false } // check each pair of items to see if they are equivalent for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // all items match, so return true return true } var tos = Stack<String>() tos.push("Swift") print(tos.items) tos.push("ジェネリック") print(tos.items) tos.push("Where 句") print(tos.items) var eos = ["Swift", "ジェネリック", "Where 句"] print(eos)

以上のプログラムを実行した出力結果は次のとおりです。

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 语句"]
["Swift", "泛型", "Where 语句"]
その他の拡張機能