English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Swift アクセス制御

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

単一のタイプ(クラス、構造体、エnum)に対してアクセスレベルを明示的に設定できます。また、これらのタイプのプロパティ、関数、初期化メソッド、基本型、インデックスなどのアクセスレベルも設定できます。

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

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

モジュールは、独立したユニットとして構築およびリリースされるフレームワークやアプリケーションを指します。Swiftのモジュールは、importキーワードを使用して別のモジュールをインポートできます。

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

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

アクセスレベル定義
public自分のモジュール内のソースファイル内のすべてのエンティティにアクセスできます。他の人がそのモジュールをインポートすることで、ソースファイル内のすべてのエンティティにアクセスできます。
internal自分のモジュール内のソースファイル内のすべてのエンティティにアクセスできますが、他の人がそのモジュール内のソースファイル内のエンティティにアクセスすることはできません。
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、もう一つの SomePrivateClass のアクセスレベルが private です。したがって、タプルのアクセスレベルの原則に従って、このタプルのアクセスレベルは private です(タプルのアクセスレベルはタプル内でアクセスレベルが最も低いタイプと一致します)。

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

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 関数の実装
}

public や internal に宣言するのは誤りです。なぜなら、その場合には private レベルの返値にアクセスできないからです。

エnumeratorの型アクセス権限

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

オンラインサンプル

以下の例では、エnumerator 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("スーパークラス")
    }
}
 
// アクセスレベルはスーパークラスを上回ってはなりません internal > public
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()

ゲッターとセットタのアクセス権限

定数、変数、属性、インデックスのゲッターやセットタのアクセスレベルは、それらの所属するメンバーのアクセスレベルを継承します。

セットタのアクセスレベルはゲッターより低く設定できるため、変数、属性、インデックスの読み書き権限を制御できます。

オンラインサンプル

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

カウンターのアクセスレベルは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(no"1: Int)
}
 
public class MainClass {
    var no1: Int // ローカルストレージ
    init(no"1: Int) {
        self.no1 = no1 // 初期化
    }
}
 
class SubClass: MainClass, TcpProtocol {
    var no2: Int
    init(no"1: Int, no2 : Int) {
        self.no2 = no2
        super.init(no"1:no1)
    }
    
    // 便利メソッドにはパラメータが1つのみ必要です
    required override convenience init(no"1: Int)  {
        self.init(no"1:no1, no2:0)
    }
}
 
let res = MainClass(no"1: 20)
let show = SubClass(no"1: 30, no2: 50)
 
print("res is: \(res.no"1)")
print("res is: \(show.no"1)")
print("res is: \(show.no"2)")

以下のプログラムの実行結果は:

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

拡張アクセス権限

許可された場合、クラス、構造体、エnumを拡張することができます。拡張メンバーは、元のクラスメンバーと同じアクセスレベルを持つべきです。例えば、公共の型を拡張した場合、追加のメンバーは元のメンバーと同じデフォルトの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 {
    // Stack<T>のオリジナル実装
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
    
    // Containerプロトコルの適合
    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 {
        // 両方のコンテナが同じ数のアイテムを含んでいることを確認してください
        someContainer.count != anotherContainer.count {
            return false
        }
        
        // 各アイテムのペアを確認して、等しいかどうかを確認してください
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        
        // すべてのアイテムが一致するため、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 文"]