Swift -プロトコル

8313 ワード

プロトコルは、それらの要件の実際の実装を提供するためにクラス、構造または列挙によって採用されることができるメソッド、特性、および他の要件の青写真を定義します.プロトコルの要件を満たすどんなタイプも、そのプロトコルに従うと言われています.
  • プロトコルは、それ自体によって指定された要件のいくつかを実装するために拡張されたり、追加の機能を実装することができます.
  • protocolは以下の要件を指定できる:
  • プロパティ要件
  • メソッド要件
  • 変異法要求事項
  • 初期化要件( INCL Failable Initializer )
  • プロパティ要件

  • プロトコルは、任意の準拠型を特定の名前と型を使用してインスタンスプロパティまたはTypeプロパティを提供する必要があります.
  • これは、各プロパティがGetTableまたはGetTableで設定可能であるかどうかを指定します.
  • プロパティが格納されているプロパティまたは計算されたプロパティであるかどうかを指定しません(一致する型は、いずれかとして実装することができます)
  • プロトコルにプロパティが必要な場合

  • GetTableおよびSettable - Conformity型は定数格納プロパティまたは読み取り専用計算プロパティを使用できません.

  • GetTable -準拠型のみの任意の種類のプロパティを使用できます.
  • プロパティの要件は常にVARキーワードを前にした変数プロパティとして宣言されます.ただし、必要に応じてプロパティを定数として宣言できます.
  • 例:
    protocol someProtocol{
        var property : Int { get }
    }
    
    class someClass : someProtocol{
        let property : Int
    
        init( _ property : Int){
            self.property = property
        }
    }
    
  • Typeプロパティの場合、静的キーワードを使用します.
  • メソッド要件


    プロトコルは、任意の適合型をインスタンスメソッドと型メソッドを実装する必要があります.
  • プロトコルの定義の中でメソッドパラメタのためにデフォルト値を指定できません.
  • Typeメソッドでは、静的キーワードを使用します.
  • 例:
    protocol RandomNumberGenerator {
        func random() -> Double
    }
    

    メソッドの要件の変異


    プロトコルは、プロトコルの定義の一部としてメソッドをマーキングすることによってメソッドをマーキングすることによって少しの適合しているタイプを実装するのを必要とすることができます.
    例:
    protocol Togglable {
        mutating func toggle()
    }
    
    enum OnOffSwitch: Togglable {
        case off, on
        mutating func toggle() {
            switch self {
            case .off:
                self = .on
            case .on:
                self = .off
            }
        }
    }
    var lightSwitch = OnOffSwitch.off
    lightSwitch.toggle()
    
  • クラスのメソッドの実装を記述するときにキーワードをつぶす必要はありません.
  • 初期化要件


    プロトコルは任意の適合型を特定の初期化器を実装することを必要とすることができます.
  • タイプは、指定された初期化装置または便宜initailizerとしてプロトコル初期化器要件に従うことができます.どちらの場合も修飾子が必要です.
  • 必須修飾子は、一致するクラスのすべてのサブクラスに対して初期化子要件の明示的または継承的な実装を提供することを保証します.
  • 最終修飾子が付いているクラスには必須の修飾子は必要ありません.
  • サブクラスがスーパークラスから指定された初期化子を上書きして、また、プロトコルから一致している初期化要件を実装するならば、必要とoverride修飾子を使用してください
  • 例:
    protocol SomeProtocol {
        init()
    }
    
    class SomeSuperClass {
        init() {
            // initializer implementation goes here
        }
    }
    
    class SomeSubClass: SomeSuperClass, SomeProtocol {
        required override init() {
            // initializer implementation goes here
        }
    }
    
  • プロトコルは、適合するタイプのための失敗しうる初期化装置要件を定義できます.
  • 失敗する初期化子要件は、適合型で失敗したか失敗した初期化子によって満たされることができます.失敗していない初期化子要件は、非失敗の初期化子または暗黙のunwrapされた失敗可能な初期化子によって満足されることができます.
  • 型としてのプロトコル


    他のタイプが許されている多くの場所で、プロトコルを型として使用できます.
  • 関数、メソッド、または初期化子のパラメーター型または戻り値の型です.
  • 定数、変数、またはプロパティの型として.
  • 配列、辞書、またはその他のコンテナの項目の種類として.
  • サブクラスにスーパークラスのダウンキャストと同じ方法で、基になる型にプロトコルタイプをダウンキャストできます.
  • 拡張子を持つプロトコル適合性の追加


  • 既存の型のソースコードへのアクセスがない場合でも、既存の型を拡張して新しいプロトコルに準拠できます.

  • 型の既存のインスタンスは、その一致が拡張のインスタンスの型に加えられるとき、自動的にプロトコルに採用して、適合します.
  • 条件付きでプロトコルに従う


    ジェネリック型は、型のジェネリックパラメーターがプロトコルに準拠するときなど、特定の条件でのみプロトコルの要件を満たすことができます.
    ジェネリック型は、一般的なWHERE句を含むことによって型を拡張するときに制約をリストアップすることによってプロトコルに適合します
    例:
    class Dice {
        let sides: Int
        let generator: RandomNumberGenerator
        init(sides: Int, generator: RandomNumberGenerator) {
            self.sides = sides
            self.generator = generator
        }
        func roll() -> Int {
            return Int(generator.random() * Double(sides)) + 1
        }
    }
    
    var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
    
    protocol TextRepresentable {
        var textualDescription: String { get }
    }
    
    extension Dice: TextRepresentable {
        var textualDescription: String {
            return "A \(sides)-sided dice"
        }
    }
    
    let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
    
    extension Array: TextRepresentable where Element: TextRepresentable {
        var textualDescription: String {
            let itemsAsText = self.map { $0.textualDescription }
            return "[" + itemsAsText.joined(separator: ", ") + "]"
        }
    }
    let myDice = [d6, d12]
    print(myDice.textualDescription) // Prints "[A 6-sided dice, A 12-sided dice]"
    
    

    拡張子を持つプロトコルの採用の宣言


    型が既にプロトコルのすべての要件に準拠しているが、まだそのプロトコルを採用しているとはまだ述べられていない場合、空の拡張子を持つプロトコルを採用できます.
    例:
    struct Hamster {
        var name: String
        var textualDescription: String {
            return "A hamster named \(name)"
        }
    }
    extension Hamster: TextRepresentable {}
    

    合成実装を用いたプロトコルの採用


    Swiftは自動的に等化可能な、ハッシュ可能で、多くの簡単なケースで同等のプロトコル適合を提供することができます.
    Swiftは、次の種類のカスタムタイプに対してイコブルブルの合成実装を提供します.
  • Equatableプロトコルに従うプロパティを格納している構造体
  • 等価プロトコルに対応する関連する型のみを持つ列挙体
  • 関連する型を持たない列挙体
  • Swiftには、次の種類のカスタムタイプのHashableの合成実装が用意されています.
  • Hashableプロトコルに従うプロパティを格納している構造体
  • Hashableプロトコルに準拠する型を持つ列挙体
  • 関連する型を持たない列挙体
  • Swiftは以下のように実装されている
  • 生の値を持たない列挙体.
  • 列挙体が型を関連付けられている場合、それらはすべて同じプロトコルに準拠している必要があります.
  • プロトコルは1つ以上の他のプロトコルを継承することができて、それが継承する要件の上に更なる要件を加えることができます.
  • プロトコルの継承リストにAnyObjectプロトコルを追加することによって、プロトコルの型をクラス型(および構造体や列挙体ではない)に制限することができます.
  • 例:
    protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
        // class-only protocol definition goes here
    }
    

  • プロトコル合成-複数のプロトコルは、アンパサンド(.)でそれらを分離することによって、単一の要件に結合されることができます.
  • 例:
    protocol Named {
        var name: String { get }
    }
    protocol Aged {
        var age: Int { get }
    }
    struct Person: Named, Aged {
        var name: String
        var age: Int
    }
    func wishHappyBirthday(to celebrator: Named & Aged) {
        print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
    }
    

    オプションプロトコル要件

  • オプションの要件はプロトコルで定義できます.これらの要件は、プロトコルに従う型によって実装される必要はありません.オプションの修飾子は
  • オプションの要件は、目的のCと対話するコードを書くことができるように、プロトコルとオプションの要件は@ objc属性でマークされなければなりません.注意@objc プロトコルは、目的Cクラスまたは他の@ objCクラスから継承するクラスだけで採用することができます.彼らは構造や数え上げでは採用できない.
  • オプションのプロトコル要件を任意の連鎖で呼び出すことができます.
  • 例:
    @objc protocol CounterDataSource {
        @objc optional func increment(forCount count: Int) -> Int
        @objc optional var fixedIncrement: Int { get }
    }
    
    class ThreeSource: NSObject, CounterDataSource {
        let fixedIncrement = 3
    }
    
    class TowardsZeroSource: NSObject, CounterDataSource {
        func increment(forCount count: Int) -> Int {
            if count == 0 {
                return 0
            } else if count < 0 {
                return 1
            } else {
                return -1
            }
        }
    }
    
    class Counter {
        var count = 0
        var dataSource: CounterDataSource?
        func increment() {
            if let amount = dataSource?.increment?(forCount: count) {
                count += amount
            } else if let amount = dataSource?.fixedIncrement {
                count += amount
            }
        }
    }
    
    var counter = Counter()
    counter.dataSource = ThreeSource()
    counter.increment(). // 3
    counter.dataSource = TowardsZeroSource()
    counter.increment(). // 2
    

    プロトコル拡張

  • プロトコルは、メソッド、初期化子、subscript、および計算されたプロパティの実装を型に適合させるために拡張できます.
  • 例:
    protocol RandomNumberGenerator {
        func random() -> Double
    }
    
    extension RandomNumberGenerator {
        func randomBool() -> Bool {
            return random() > 0.5
        }
    }
    
  • プロトコル拡張は実装を型に合わせることができますが、プロトコルを広げることができないか、別のプロトコルから継承することができません.
  • エクステンションのメソッドとプロパティが一般的なWHERE句を使用して使用できる前に、一致する型に対して制約を指定できます.
  • 例:
    extension Collection where Element: Equatable {
        func allEqual() -> Bool {
            for element in self {
                if element != self.first {
                    return false
                }
            }
            return true
        }
    }
    
    適合型が同じ方法またはプロパティのための実装を提供する複数の制約付き拡張の要件を満たす場合、Swiftは最も特殊な制約に対応する実装を使用します.