Swift 6.0 beta 日本語化計画 : Swift 6.0 beta
プロトコル
プロトコル は、特定のタスクや機能の一部に適するメソッド、プロパティ、およびその他の要件の青写真を定義します。そしてプロトコルは、それらの要件の実際の実装を提供するために、クラス、構造体、または列挙型によって 採用 される事ができます。プロトコルの要件を満たす全ての型は、そのプロトコルに 準拠する と言われます。
準拠する型が実装しなければならない要件を指定する事に加え、プロトコルを拡張して、これらの要件の一部を実装するか、または準拠する型が利用できる追加の機能を実装できます。
クラス、構造体、列挙型と非常に似た方法でプロトコルを定義できます。
カスタム型は、特定のプロトコルを採用すると述べるのに、それらの定義の一部として、プロトコルの名前をコロンで区切って型の名前の後に書きます。複数のプロトコルを一覧表示でき、カンマで区切られます。
クラスがスーパークラスを持っている場合、それが採用する全てのプロトコルの前にスーパークラス名を一覧表示し、コンマを続けます。
プロトコルは、インスタンスプロパティや、特定の名前と型を持つ型プロパティを提供するために、全ての準拠する型に要求できます。プロトコルはプロパティが格納されたプロパティか計算されたプロパティかを指定する必要はありません–必要なプロパティ名と型だけを指定します。プロトコルはまた、各プロパティが取得可能か、取得可能 かつ 設定可能かのいずれかでなければならないと指定します。
プロトコルが取得可能かつ設定可能なプロパティを必要とする場合、そのプロパティの要件は、定数の格納されたプロパティまたは読み取り専用の計算されたプロパティで満たすことはできません。プロトコルがプロパティが取得可能であることのみを必要とする場合、要件は、プロパティの全ての種類によって満たされ、自分のコードに便利であればプロパティは設定可能であっても有効です。
プロパティの要件は、常に var キーワードの接頭辞で始まる変数プロパティとして宣言されます。取得可能かつ設定可能なプロパティは、その型宣言の後に { get set } と書くことで示され、取得可能プロパティは { get } と書くことで示されます。
プロトコル内で定義した時には、static のキーワードの前に、必ず型プロパティの要件を付けます。この規則は、型プロパティ要件がクラスによって実装されて class や static キーワードが接頭辞に付けていてもあてはまります:
ここでは一つのインスタンス・プロパティ要件を持つプロトコルの例を示します。
FullyNamed プロトコルは、完全に修飾された名前を提供するために準拠する型を必要とします。プロトコルは準拠する型の性質について他のものは指定しません–それはその型がフルネームをそれ自身で提供できなければならないことのみを指定します。プロトコルはどんな FullyNamed 型でも String 型の fullName と言う取得可能なインスタンス・プロパティを持っていなければならないことを述べています。
ここで FullyNamed プロトコルに準拠し、採用する簡単な構造体の例を示します。
この例では、特定の名前の人を表す Person と言う構造体を定義しています。それは、その定義の最初の行の一部として FullyNamed プロトコルを採用していると述べています。
Person の各インスタンスには、String 型の fullName と言う一つの格納されたプロパティがあります。これは FullyNamed プロトコルの一つの要件に一致し、Person が正しくプロトコルに準拠していることを意味します。(プロトコル要件が満たされていない場合、Swift はコンパイル時にエラーを報告します。)
ここで FullyNamed プロトコルに準拠し採用した、より複雑なクラスを挙げます。
このクラスは、宇宙船の、計算された読み取り専用のプロパティとして fullName プロパティの要件を実装しています。各 Starship クラスインスタンスは、必須の name と optional の prefix を格納します。fullName プロパティは、存在する場合は prefix 値を使用し、宇宙船のフルネームを作成するために、name の先頭にそれを付けます。
プロトコルは特定のインスタンスメソッドと型メソッドを型に準拠することで実装される事を要求できます。これらのメソッドは、通常のインスタンスや型メソッドとまったく同じ方法で、プロトコルの定義の一部として書かれますが、中括弧やメソッド本体はありません。可変個のパラメータは、通常のメソッドの場合と同じ規則に従い、許可されています。しかし、デフォルト値はプロトコルの定義の中のメソッドパラメータとしては指定できません。
型プロパティの要件と同様に、これらがプロトコル内で定義されている場合、static キーワードを型メソッドの要件の前に接頭辞として常に付けて下さい。型メソッド要件がクラスによって実装されていて class または static キーワードが接頭辞として前に付いていてもこれは当てはまります。
以下の例では、一つのインスタンスメソッド要件を持つプロトコルを定義しています。
このプロトコル、RandomNumberGenerator は、それが呼び出されるたびに Double 値を返す random と言うインスタンスメソッドを持つ、任意の準拠する型が必要です。それはプロトコルの一部として指定されていませんが、この値は最小 0.0 から最大 1.0 (を含まない) までの数であると仮定されています。
RandomNumberGenerator プロトコルは、各乱数がどう発生されるかについての仮定は全くしていません。単に新しい乱数を発生するための標準的な方法を提供する発生プログラムが必要です。
ここで RandomNumberGenerator プロトコルを採用し準拠したクラスの実装を示します。このクラスは、線形合同発生法 として知られている擬似乱数生成アルゴリズムを実装しています。
メソッドにとって、それが属するインスタンスを変更 (または 変異) することが必要なことがあります。値型 (つまり、構造体と列挙型) のインスタンスメソッドの場合、メソッドは、そのインスタンスの全てのプロパティおよびそれが属するインスタンスを変更することが許可されていることを示すために、メソッドの func キーワードの前に、mutating キーワードを配置します。このプロセスは、インスタンスメソッド内から値型を変更 で説明しました。
プロトコルを採用する、全ての型のインスタンスを変異させることを意図しているプロトコル・インスタンス・メソッドの要件を定義する場合、プロトコルの定義の一部として mutating キーワードでメソッドをマークします。これは、プロトコルを採用し、そのメソッドの要件を満たす事を構造体や列挙型に可能にします。
以下の例は、toggle と言う一つのインスタンス・メソッドの要件を定義する、Togglable と言うプロトコルを定義しています。その名前が示唆するように、toggle( ) メソッドは、典型的には、その型のプロパティを変更することによって、トグルまたは任意の準拠型の状態を反転することを意図しています。
toggle( ) メソッドは Togglable プロトコル定義の一部として mutating キーワードでマークされていますが、それが呼び出された時、メソッドが準拠するインスタンスの状態を変異させることが期待されていることを示すためです:
構造体または列挙型のため Togglable プロトコルを実装する場合、その構造体または列挙型も mutating としてマークされている toggle( ) メソッドの実装を提供することにより、プロトコルに準拠することができます。
以下の例は、OnOffSwitch と呼言う列挙型を定義します。この列挙型は、列挙型の case の on と off で示される二つの状態の間を切り替えます。列挙型の toggle の実装は Togglable プロトコルの要件に一致させるため、 mutating としてマークされています:
プロトコルは準拠する型によって実装されるべき特定のイニシャライザを必要とします。中括弧またはイニシャライザ本体なしで、通常のイニシャライザと完全に同じ方法で、プロトコルの定義の一部としてこれらのイニシャライザを記述して下さい。
指定イニシャライザやコンビニエンスイニシャライザのいずれかとして準拠するクラスでプロトコル・イニシャライザの要件を実装できます。どちらの場合でも、required 修飾子でイニシャライザの実装をマークしなければなりません。
required 修飾子の使用は、それらがまた、プロトコルに準拠するように、準拠するクラスのすべてのサブクラス上のイニシャライザ要件の明示的または継承された実装を提供することを保証します。
必須イニシャライザの詳細については、必須イニシャライザ を参照してください。
サブクラスがスーパークラスからの指定イニシャライザを上書きし、また、プロトコルから一致するイニシャライザの要件を実装する場合、required と override 修飾子の両方で、イニシャライザの実装をマークして下さい。
失敗可能イニシャライザ で定義されているように、プロトコルは、準拠する型の失敗可能イニシャライザの要件を定義できます。
失敗可能イニシャライザの要件は準拠する型での失敗可能または失敗不可能なイニシャライザによって満たされます。失敗不可能イニシャライザの要件は失敗不可能イニシャライザまたは暗黙的に開封された失敗可能イニシャライザによって満たされます。
プロトコル自体は、実際には、何も機能を実装しません。それにもかかわらず、あなたのコード内でプロトコルを型として使用できます。
プロトコルを型として使用する最も一般的な方法は、プロトコルをジェネリック (汎用) 制約として使用することです。汎用制約を持つコードは、プロトコルに準拠する任意の型で動作し、特定の型は API を使用するコードによって選択されます。たとえば、引数を取る関数を呼び出し、その引数の型が汎用である場合、呼び出し元が型を選択します。
不透明型を使用したコードは、プロトコルに準拠する何らかの型で動作します。基礎となる型はコンパイル時に判明しており、API 実装ではその型を選択しますが、その型の ID は API のクライアントからは隠されています。不透明型を使用すると、API の実装の詳細が抽象化の層を通じて漏洩するのを防ぐことができます。たとえば、関数から特定の戻り値の型を隠し、値が特定のプロトコルに準拠することだけを保証することができます。
ボックス化されたプロトコル型を持つコードは、実行時に選択された、プロトコルに準拠する任意の型で動作します。この実行時の柔軟性をサポートするために、Swift は必要に応じて間接レベルを追加します。これは ボックス と呼ばれ、パフォーマンスコストがかかります。この柔軟性により、Swift はコンパイル時に基礎となる型を認識しないため、プロトコルに必要なメンバにのみアクセスできます。基礎となる型の他の API にアクセスするには、実行時にキャストする必要があります。
プロトコルを汎用制約として使用する方法については、ジェネリック(汎用) を参照してください。不透明型とボックス化されたプロトコル型の詳細については、不透明型とボックス化された型 を参照してください。
デリゲート は、別の型のインスタンスへその責任の一部を渡す (又は デリゲート する) 事をクラスや構造体に可能にするデザインパターンです。このデザインパターンは、デリゲートされた責任をカプセル化するプロトコルを定義することによって実装され、準拠する型 (デリゲートとして知られる) がデリゲートされた機能を提供することが保証されるようにします。デリゲートは、特定のアクションに応答するために、またはそのソースの基本型を知らなくても、外部ソースからデータを取得するために使用できます。
以下の例では、サイコロのゲームと、ゲームの進行状況を追跡するデリゲートのネストされたプロトコルを定義しています。
DiceGame クラスは、各プレイヤーが順番にサイコロを振り、最も大きい数字を出したプレイヤーがそのラウンドを勝ち取るゲームを実装します。このクラスは、この章の前半の例の線形合同型ジェネレータを使用して、サイコロを振るための乱数を生成します。
DiceGame.Delegate プロトコルは、サイコロゲームの進行状況を追跡するために採用できます。DiceGame.Delegate プロトコルは常にサイコロゲームの文脈で使用されるため、DiceGame クラス内にネストされます。プロトコルは、外側の宣言がジェネリックでない限り、構造体やクラスなどの型宣言内にネストできます。ネストした型の詳細については、ネストした型 を参照してください。
強い循環参照を防ぐために、デリゲートは弱い参照として宣言されます。弱い参照の詳細については、クラスインスタンス間の強い循環参照 を参照してください。プロトコルをクラス専用としてマークすると、DiceGame クラスはそのデリゲートが弱い参照を使用しなければならないことを宣言できます。クラス専用のプロトコルは、クラス専用プロトコル で説明しているように、AnyObject からの継承によってマークされます。
DiceGame.Delegate は、ゲームの進行状況を追跡するための 3 つのメソッドを提供します。これらの 3 つのメソッドは、上記の play(rounds:) メソッドのゲーム ロジックに組み込まれています。DiceGame クラスは、新しいゲームの開始時、新しい順番の開始時、またはゲームの終了時に、デリゲートメソッドを呼び出します。
delegate プロパティは optional の DiceGame.Delegate であるため、Optional の連鎖 で説明しているように、play(rounds:) メソッドはデリゲートのメソッドを呼び出すたびに Optional の連鎖を使用します。delegate プロパティが nil の場合、これらのデリゲート呼び出しは無視されます。delegate プロパティが nil 以外の場合、デリゲートメソッドが呼び出され、DiceGame インスタンスがパラメータとして渡されます。
次の例は、DiceGame.Delegate プロトコルを採用した DiceGameTracker というクラスを示しています。
DiceGameTracker クラスは、DiceGame.Delegate プロトコルに必要な 3 つのメソッドをすべて実装します。これらのメソッドを使用して、新しいゲームの開始時に両方のプレイヤーのスコアをゼロにし、各ラウンドの終了時にスコアを更新し、ゲームの終了時に勝者を発表します。
DiceGame と DiceGameTracker の実際の動作は以下のとおりです。
既存の型のソースコードへのアクセス権を持っていない場合でも、新しいプロトコルに準拠し、採用するために、既存の型を拡張することができます。拡張機能は、既存の型に新しいプロパティ、メソッド、およびサブスクリプトを追加し、そのためプロトコルが要求する全ての要件を追加できます。拡張機能の詳細については、拡張機能 を参照してください。
例えば、TextRepresentable と言うこのプロトコルは、テキストとして表現される方法がある、全ての型で実装される事ができます。これは、それ自体の説明、または現在の状態のテキストバージョンです。
上記の Dice クラスは TextRepresentable を採用し、準拠するように拡張できます:
この拡張機能は、Dice がその元の実装で提供していたのとまったく同じ方法で新しいプロトコルを採用します。プロトコル名は、コロンで区切られ、型名の後に提供され、プロトコルのすべての要件の実装は、拡張機能の中括弧内に提供されます。
全ての Dice インスタンスは、今や TextRepresentable として扱えるようになります:
同様に、SnakesAndLadders ゲームクラスは TextRepresentable プロトコルを採用し、準拠するように拡張できます:
汎用型は、その型の汎用パラメータがプロトコルに準拠している場合など、特定の条件下でのみプロトコルの要件を満たすことができます。型を拡張するときに制約をリストアップすることによって、汎用型を条件付きででプロトコルに準拠させることができます。汎用の where 句を書くことで、採用しているプロトコルの名前の後にこれらの制約を書いてください。汎用の where 句について詳しくは、汎用の where 句 を参照してください。
以下の拡張機能は、それらが TextRepresentable に準拠する型の要素を格納するときはいつでも、Array インスタンスを TextRepresentable プロトコルに準拠させます。
型がすでにプロトコル要件のすべてに準拠していても、まだそれは、そのプロトコルを採用すると述べていない場合、空の拡張機能を持つプロトコルを採用してそれができます。
TextRepresentable が必須の型ならどこでも Hamster のインスタンスは、使用できるようになります。
Swift は、多くの単純な場合、Equatable、Hashable、および Comparable のプロトコル準拠を自動的に提供できます。この合成された実装を使用すると、プロトコル要件を自分で実装するために繰り返し定型コードを記述する必要がなくなります。
Swift は、以下の種類のカスタム型に対して Equatable の合成された実装を提供します。
== の合成された実装を受け取るには、== 演算子を自分自身で実装せずに、元の宣言を含むファイルで Equatable への準拠を宣言します。Equatable プロトコルは、!= のデフォルトの実装を提供します。
以下の例では、Vector2D 構造体と同様に、3次元位置ベクトル (x、y、z) の Vector3D 構造体を定義しています。x、y、 および z プロパティはすべて Equatable 型であるため、Vector3D は等価演算子の合成された実装を受け取ります。
Swift は、以下の種類のカスタム型用に Hashable の合成された実装を提供します。
hash(into:) の合成された実装を受け取るには、hash(into:) メソッドを自分で実装せずに、元の宣言を含むファイル内で Hashable への準拠を宣言します。
Swift は、生の値を持たない列挙型の Comparable の合成された実装を提供します。列挙型に関連した型がある場合、それらはすべて Comparable プロトコルに準拠していなければなりません。< の合成された実装を受け取るには、< の演算子を自分自身で実装せずに、元の列挙型の宣言を含むファイル内で Comparable への準拠を宣言します。Comparable プロトコルのデフォルトの実装である <=、>、および >= は、残りの比較演算子を提供します。
以下の例では、初心者、中級者、および熟練者向けの caase を含む SkillLevel 列挙型を定義しています。熟練者はさらに、持っている星の数によってランク付けされます。
型としてのプロトコル で述べたように、プロトコルは、型として配列や辞書などのようなコレクションに格納されて使用できます。以下の例では、TextRepresentable の things の配列を作成します:
配列内の項目を反復処理し、各項目のテキスト表現を印刷することが可能になりました。
thing 定数が TextRepresentable 型であることに注意してください。舞台裏では実際のインスタンスはそれらの型の一つであっても、Dice、DiceGame、または Hamster 型のいずれでもありません。それにもかかわらず、それは TextRepresentable 型なので、TextRepresentable である全てには texualDescription プロパティがあることが知られているため、ループを通るたび thing.texualDescription にアクセスするのは安全です。
プロトコルは、1つ以上の他のプロトコルを 継承 でき、それが継承する要件の上にさらに要件を追加できます。プロトコルの継承の構文は、クラス継承の構文に似ていますが、オプションで、カンマで区切られた複数の継承プロトコルを一覧表示します:
ここでは上に挙げた例から TextRepresentable プロトコルを継承するプロトコルの例を示します。
この例では、TextRepresentable から継承する新しいプロトコル、PrettyTextRepresentable を定義しています。PrettyTextRepresentable を採用する全てのものは TextRepresentable によって強制される要件を全て満たさねばならず、また PrettyTextRepresentable によって強制される追加要件を満たさねばなりません。この例では、 PrettyTextRepresentable は String を返す prettyTextualDescription という取得可能なプロパティを提供するための一つの要件を追加します。
SnakesAndLadders クラスは PrettyTextRepresentable を採用し、準拠するように拡張できます:
この機能拡張 (extension) は、それが PrettyTextRepresentable プロトコルを採用し、SnakesAndLadders 型のため prettyTextualDescription プロパティの実装を提供することを述べています。PrettyTextRepresentable である全てのものはまた TextRepresentable でもなければならず、それで prettyTextualDescription の実装は、出力文字列を開始する TextRepresentable プロトコルから textualDescription プロパティにアクセスすることによって開始します。これは、コロンと改行を付加し、そのかなりなテキスト表現の開始としてこれを使用しています。その後、ボードの正方形の配列を反復処理し、各正方形の内容を表現する幾何学的な形を追加します:
prettyTexualDescription プロパティは、今やあらゆる SnakesAndLadder インスタンスのかなりなテキストの説明を印刷するために使用できます。
プロトコルの継承リストに、AnyObject プロトコルを追加することによって、プロトコルの採用をクラス型 (構造体または列挙型ではない) に限定することができます。
上記の例では、SomeClassOnlyProtocol はクラス型によってのみ採用できます。SomeClassOnlyProtocol を採用しようとして構造体や列挙型の定義を書くと、コンパイル時エラーになります。
一度に複数のプロトコルに準拠する型を要求するのが便利な事があります。プロトコルの構成 で複数のプロトコルを一つの要件に組み合わせることができます。プロトコル構成は、構成内のすべてのプロトコルの要件を組み合わせた一時的なローカルプロトコルを定義したかのように動作します。プロトコル構成は、新しいプロトコル型を定義しません。
プロトコル構成は、someprotocol & AnotherProtocol の形式です。アンパサンド(&) で区切って、必要なだけ多くのプロトコルをリストできます。プロトコルのそのリストに加えて、プロトコル構成には 1 つのクラス型を含めることもでき、これを使用して必須スーパークラスを指定することができます。
ここで関数パラメータ上の一つのプロトコル構成要件に Named と Aged という2つのプロトコルを組み合わせた例を示します。
この例では、取得可能な String プロパティの name と言う一つの要件を持つ、Named プロトコルがあります。取得可能な Int 型プロパティの age と言う一つの要件がある、Aged プロトコルもあります。これらのプロトコルの両方が Person と言う構造体によって採用されています。
また、この例では、wishHappyBirthday(to:) 関数も定義しています。celebrrator パラメータの型は、Named & Aged であり、これは、"Named と Aged プロトコルの両方に準拠するすべての型" を意味します。必須プロトコルの両方に準拠している限り、どちらの特定の型が関数に渡されたかは関係ありません。
この例は次に、birthdayPerson という新しい Person インスタンスを作成し、この新しいインスタンスを wishHappyBirthday(to:) 関数に渡します。Person は両方のプロトコルに準拠しているので、この呼び出しは有効で、wishHappyBirthday(to:) 関数はその誕生日の挨拶を印刷できます。
以前の例の Named プロトコルと Location クラスを組み合わせた例を次に示します。
beginConcert(in:) 関数は Location & Named 型のパラメータをとり、これは "Location のサブクラスで Named プロトコルに準拠するすべての型" を意味します。この場合には、City が両方の要件を満たします。
Person は Location のサブクラスではないため、birthdayPerson を beginConcert(in:) 関数に渡すことは無効です。同様に、Named プロトコルに準拠していない Location のサブクラスを作成した場合、その型のインスタンスを使用して beginConcert(in:) を呼び出すことも無効です。
型キャスト で説明したように is と as 演算子を、プロトコルの準拠をチェックするため、また特定のプロトコルにキャストするため使用できます。プロトコルへのキャスト及びチェックは、型のキャストとチェックとまったく同じ構文に従います。
以下の例では、HasArea と言うプロトコルを定義し、それは area と言う Double の一つの取得可能なプロパティ要件を持っています。
ここに2つのクラス、Circle と Country があり、両方とも HasArea プロトコルに準拠しています
Circle クラスは、格納された radius プロパティに基づいて、計算されたプロパティとして area プロパティの要件を実装しています。Country クラスは格納されたプロパティとして直接 area の要件を実装しています。どちらのクラスも正しく HasArea プロトコルに準拠しています。
ここで、HasArea プロトコルに準拠していない、Animal と言うクラスがあります。
Circle、Country と Animal クラスは、共有する基本クラスを持っていません。それにもかかわらず、それらはすべてクラスであり、そして3つ全ての型のインスタンスは、型 AnyObject の値を格納する配列を初期化するために使えます:
objects 配列は、2ユニットの半径の Circle インスタンスを含む配列リテラルで初期化されます。Country インスタンスは、平方キロメートルで表したイギリスの表面積で初期化されます。そして Animal インスタンスは、4つの足で初期化されます。
objects 配列は、反復処理することができるようになり、配列内の各オブジェクトは、それが HasArea プロトコルに準拠するかどうかを確認できます。
配列内のオブジェクトが HasArea プロトコルに準拠している時はいつでも、as? 演算子で返される optional の値は、objectWithArea と言う定数に optional 結合されて開封されます。objectWithArea 定数は HasArea 型であることが知られており、したがって、その area プロパティにはアクセスでき、型安全な方法で印刷できます。
基礎となるオブジェクトは、キャストするプロセスによっては変更されないことに注意してください。それらは、Circle、Country そして Animal であり続けます。しかし、それらが objectWithArea 定数に格納された時点で、それらは HasArea 型であるとのみ知られるので、それらの area プロパティのみにアクセスできます。
プロトコルには optional の要件 を定義できます。これらの要件は、プロトコルに準拠する型で実装する必要はありません。optional の要件は、プロトコルの定義の一部として、optional の修飾子を前に付けます。optional の要件は、Objective-C と相互運用するコードを書けるように、利用できます。プロトコルおよび optional の要件は両方とも @objc 属性でマークされなければなりません。その @objc プロトコルは、クラスのみに採用され、構造体または列挙型によっては採用できません。
optional の要件内にメソッドやプロパティを使用する場合、その型は自動的に、optional になります。例えば、(Int) -> String 型のメソッドは ((Int) -> Strng)? になります。メソッドの戻り値ではなく、関数全体の型が optional に包まれていることに注意してください。
要件がプロトコルに準拠した型によって実装されなかった可能性を考慮して、optional のプロトコル要件は optional の連鎖で呼び出すことができます。someOptionalMethod?(someArgument) のように、呼び出されたときにメソッドの名前の後に疑問符を書き込むことによって、optional のメソッドの実装を確認してください。optional の連鎖の詳細については、Optional の連鎖 を参照してください。
以下の例では、その増分量を提供するために外部データソースを使用する、Counter と言う整数をカウントするクラスを定義しています。このデータソースは2つの optional の要件を持つ CounterDataSource プロトコルによって定義されます。
CounterDataSource プロトコルは increment(forCount:) と言う optional のメソッド要件と fixedIncrement と言う optional のプロパティ要件を定義しています。これらの要件は、Counter インスタンスの適切な増分量を提供するために、データソースの2つの異なる方法を定義しています。
以下に定義された Counter クラスには、CounterDataSource? 型の optional の dataSource プロパティがあります:
Counter クラスは、count と言う変数プロパティに現在の値を格納します。Counter クラスは、また increment と言うメソッドも定義しており、そのメソッドが呼び出されるたびに count プロパティを増分します。
increment( ) メソッドは、まずそのデータソースに increment(forCount:) メソッドの実装を探して増分量を取得しようとします。increment( ) メソッドは increment(forCount:) を呼び出そうとして、optional の連鎖を使用し、メソッドの一つの引数として現在の count 値を渡します。
ここで optional の連鎖の 2つの レベルが演じている事に注意してください。第一に、dataSource が nil である可能性があり、したがって dataSource が nil でない場合にのみ increment(forCount:) を呼び出す必要があることを示すために、dataSource はその名前の後に疑問符がついています。第二に、 dataSource が 存在 していて も、それは optional の要件なので、increment(forCount:) を実装していることを保証するものではありません。ここで、increment(forCount:) が実装されていない可能性も、optional の連鎖によって処理されます。increment(forCount:) への呼び出しは increment(forCount:) が存在する時にのみ起こり、すなわちそれが nil でない場合にのみ起こります。increment(forCount:) も、その名前の後に疑問符が書かれているのはこのためです。
increment(forCount:) への呼び出しは、これらの2つの理由のいずれかで失敗する可能性があるため、呼び出しは、optional の Int 値を返します。これは increment(forCount:) が CounterDataSource の定義で optional でない Int 値を返すように定義されていても同様です。2つの optional の連鎖の操作が、次々とあっても、結果はまだ一つの optional で包み込まれています。複数の optional の連鎖の操作の使用については、連鎖の複数レベルのリンク を参照して下さい。
increment(forCount:) を呼び出した後、それが返す optional の Int は、optional の結合を使用して、amount と言う定数に開封されます。optional の Int に値がある場合、すなわち、デリゲートとメソッドの両方が存在し、そしてメソッドが値を返す場合、開封された amount は格納された count プロパティに追加され、そして増分は完了します。
increment(forCount:) メソッドから値を取得でき ない 場合、それは dataSource が nil か、またはデータソースが increment(forCount:) を実装していないためのいずれかであり、その後代わりに increment( ) メソッドは、データソースの fixedIncrement プロパティから値を取得しようとします。fixedIncrement プロパティも optional の要件であり、したがって、その値も CounterDataSource プロトコル定義の一部として fixedIncrement が optional でない Int プロパティとして定義されているにもかかわらず、optional の Int 値です。
ここで、データソースが、それが照会されるたびに定数の 3 の値を返す単純な CounterDataSource を実装しましょう。optional の fixedIncrement プロパティの要件を実装することでこれを行います:
新しい Counter インスタンスのデータソースとして ThreeSource のインスタンスを使用できます。
上記のコードは新しい Counter インスタンスを作成します。そのデータソースを新しい ThreeSource インスタンスに設定します。そして counter の increment( ) メソッドを4回呼び出します。予想されるように、それぞれ increment( ) が呼ばれ、3づつ counter の count プロパティが増加します。
ここで TowardsZeroSource と言う、より複雑なデータソースがあり、これは Counter インスタンスを現在の count 値からゼロに向かってカウントアップまたはダウンします。
TowardsZeroSource クラスは、CounterDataSource プロトコルから、optional の increment(forCount:) メソッドを実装し、どの方向にカウントが行われるかを示すために count 引数値を使います。もし count がすでにゼロなら、メソッドは更なるカウントが起こらない事を示すために 0 を返します。
-4 からカウントをゼロにするために、既存の Counter インスタンスで TowardsZeroSource のインスタンスを使用できます。カウンタがゼロに達すると、それ以上のカウントは行われません。
プロトコルは、メソッド、イニシャライザ、サブスクリプト、および計算されたプロパティの実装を準拠型に提供するように拡張できます。これにより、各型の個々の準拠性やグローバル関数ではなく、プロトコル自体の動作を定義できます。
例えば、RandomNumberGenerator プロトコルは、randomBool( ) メソッドを提供するように拡張でき、これはランダムな Bool 値を返すために必要な randm( ) メソッドの結果を使用します。
プロトコル上で拡張機能を作成することにより、すべての準拠型は何も追加の変更なしに、このメソッドの実装を自動的に得ることができます。
プロトコル拡張機能では、準拠型に実装を追加できますが、プロトコルを拡張したり他のプロトコルから継承したりすることはできません。プロトコルの継承は、常にプロトコル宣言自体で指定されます。
そのプロトコルの全てのメソッドまたは計算されたプロパティ要件に、デフォルトの実装を提供するために、プロトコルの拡張機能を使用できます。準拠する型が、必要なメソッドやプロパティの独自の実装を提供している場合、その実装は、拡張機能が提供するものの代わりに使用されます。
例えば、PrettyTextRepresentable プロトコルは、その必須 prettyTextualDescription プロパティのデフォルトの実装を提供できる TextRepresentable プロトコルを継承しており、単に textualDescription プロパティにアクセスした結果を返します:
プロトコル拡張機能を定義するときは、拡張機能のメソッドとプロパティが利用可能になる前に、準拠した型が満たさなければならない制約を指定できます。これらの制約は、拡張するプロトコルの名前の後に、汎用の where 句を記述して行います。汎用の where 句の詳細については、汎用の Where 句 をご覧下さい。
たとえば、その要素が Equatable プロトコルに準拠する任意のコレクションに適用される Collection プロトコルへの拡張機能を定義できます。コレクションの要素を Swift の標準ライブラリの一部である Equatable プロトコルに制約することで、== と != 演算子を使用して、2 つの要素間の等価性と不等価性を確認できます。
allEqual( ) メソッドは、コレクション内のすべての要素が等しい場合にのみ true を返します。
整数の 2 つの配列を考えてみましょう。1 つはすべての要素が同じで、もう 1 つはそうでないものです。
2つの配列は Collection に準拠し、整数は Equatable に準拠するため、equalNumbers および differentNumbers は allEqual( ) メソッドを使用できます。