Swift 6.0 beta 日本語化計画 : Swift 6.0 beta
Optional の連鎖
Optional の連鎖 は、今は nil かもしれない optional のプロパティ、メソッド、およびサブスクリプトを照会し、呼び出すためのプロセスです。optional が値を含んでいる場合は、プロパティ、メソッド、またはサブスクリプトの呼び出しは成功します。optional が nil の場合、プロパティ、メソッド、またはサブスクリプトの呼び出しは nil を返します。複数の照会を一緒に連鎖することができ、全体の連鎖は、連鎖内のどれかのリンクが nil の場合、綺麗に失敗します。
optional が nil でない場合、プロパティ、メソッドまたはサブスクリプトを呼び出したい optional の値の後に疑問符(?) を置くことで、optional の連鎖を指定できます。これは、その値の開封を強制するために、optional の値の後に感嘆符(!) を置くことに非常に似ています。主な違いは、optional が nil の時には optional の連鎖が綺麗に失敗するのに対し、強制開封は、optional が nil の時実行時エラーを引き起こす事です。
optional の連鎖が nil 値で呼び出しうるという事実を反映するために、optional の連鎖呼び出しの結果は、照会しているプロパティ、メソッド、またはサブスクリプトが optional でない値を返しても、常に optional 値になります。この optional の戻り値を使用して、optional 連鎖の呼び出しが成功したか (返された optional に値が含まれているか)、連鎖の nil 値のために成功しなかったかどうか (返された optional 値が nil か) をチェックすることができます。
具体的には、optional の連鎖の呼び出しの結果は、期待される戻り値と同じ型ですが、optional に包まれています。通常は Int として返すプロパティは optional の連鎖を通してアクセスされる時 Int? で返ります。
以下に挙げるいくつかのコードスニペットでは、どのように optional の 連鎖が強制開封とは異なり、成功を確認できるかについて説明しています。
まず、Person と Residence と言う2つのクラスが定義されます。
Residence インスタンスは 1 の デフォルト値を持つ numberOfRooms と言う一つの Int プロパティを持っています。Person インスタンスは Residence? 型の optional の residence プロパティ値を持っています
新しい Person インスタンスを作成した場合、その residence プロパティは、optional であるおかげで、nil にデフォルトで初期化されます。以下に示すコードでは、john は nil の residence プロパティ値を持っています。
その値の開封を強制的に行うため、residence の後に感嘆符を置くことによって、この person の residence の numberOfRooms プロパティにアクセスしようとすると、開封するべき residence 値がないので、実行時エラーを引き起こします:
john.residence は nil でない値を有しており、部屋の適切な数を含む Int 値に roomCount を設定したときには上記のコードは、成功します。しかし、上に示したように residence は、nil なので、このコードは常に実行時エラーを引き起こします。
Optional の連鎖は numberOfRooms の値にアクセスする別の方法を提供します。optional の連鎖を使用するには、感嘆符(!)の代わりに疑問符(?)を使用して下さい。
これは、Swift に、optional の residence プロパティの"連鎖" と、 residence が存在する場合、numberOfRooms の値を取得するように指示します。
numberOfRooms にアクセスする試みが失敗する可能性があるため、optional の連鎖の試みは、Int? 型または "optional Int" 型の値を返します。residence が nil の時、上記の例のように、この optional の Int はまた、numberOfRooms にアクセスできなかったという事実を反映して、nil になります。optional の Int は、整数を開封して optional でない値を roomCount 定数に代入するための optional の結合によってアクセスされます。
これは、numberOfRooms が optional でない Int であったとしても正しいことに注意してください。それが、optional の連鎖を介して照会されているという事実は、numberOfRooms への呼び出しは常に Int の代わりに Int? を返すことを意味します。
Residence インスタンスを、john.residence に代入でき、そのためそれはもはや nil 値を持ちません。
john.residence は今や、むしろ nil よりも、実際の Residence インスタンスを含んでいます。以前と同じ optional の連鎖で numberOfRooms にアクセスしようとすると、それは今や numberOfRooms のデフォルト値 1 を含む Int? を返します。
一つ以上のレベルの深さのプロパティ、メソッド、およびサブスクリプトへの呼び出しで、optional の連鎖を使用できます。これは、相互に関連する型の複雑なモデル内のサブプロパティに下へと貫いていき、それらのサブプロパティのプロパティ、メソッド、およびサブスクリプトにアクセスできるかどうかを確認できます。
以下のコードスニペットは、マルチレベルの optional の連鎖の例を含む、いくつか後の例で使用する、4つのモデルクラスを定義しています。これらのクラスは、プロパティ、メソッド、およびサブスクリプトと関連した、Room と Address クラスを追加することにより、上記に示した Person と Residence モデルを発展させています。
Person クラスは、以前と同じ方法で定義されます。
Residence クラスは、以前よりも複雑です。今回は、Residence クラスは [Room] 型の空の配列で初期化される、rooms と言う変数プロパティを定義しています。
Residence のこのバージョンは、Room インスタンスの配列を格納しているため、その numberOfRooms プロパティは格納されたプロパティではなく、計算されたプロパティとして実装されています。計算された numberOfRooms プロパティは、単に rooms 配列から count プロパティの値を返します。
その rooms 配列にアクセスする近道として、このバージョンの Residence では、rooms 配列内の要求されたインデックスの room へのアクセスを提供する読み書きできるサブスクリプトを提供しています。
Residence のこのバージョンはまた、printNumberOfRooms と言うメソッドも提供し、単に residence の部屋 (rooms) の数を印刷します。
最後に、Residence は、Address? の型で address と言う optional のプロパティを定義しています。このプロパティの Address クラスの型は以下に定義されています。
rooms 配列に使用される Room クラスは、name と言う1つのプロパティと、そのプロパティを適切な room の name (部屋の名前) に設定するイニシャライザを持つ単純なクラスです。
このモデル内の最後のクラスは、Address と言います。このクラスには、String? 型の3つの optional のプロパティがあります。最初の2つのプロパティ、buildingName と buildingNumber は、address (住所) の一部として特定の建物を識別するための別の方法です。第3のプロパティ、street は、その住所 (address) の通り (street) に名前を付けるために使用されます。
Address クラスはまた、String? の戻り値型を持っている buildingIdentifier( ) と言うメソッドも提供します。このメソッドは、address のプロパティをチェックし、それが値を持っていれば、buildingName を返し、または buildingNumber と繋ぎ合わされた street の両方が値を持っている場合は、それらを返し、そうでなければ nil を返します。
強制開封の代替としての Optional の連鎖 で示したように、optional の値を持つプロパティにアクセスするには、optional の連鎖を使用でき、またそのプロパティへのアクセスが成功したかどうかをチェックできます。
新しい Person インスタンスを作成するには上記のように定義されたクラスを使用し、以前のようにその numberOfRooms プロパティにアクセスしてみて下さい。
john.residence は nil であるため、この optional の連鎖の呼び出しは以前と同じように失敗します。
また、optional の連鎖を通じてプロパティの値を設定しようとすることもできます:
この例では、john.residence は現在 nil であるため、john.residence の address プロパティを設定しようとする試みは失敗します。
代入は、optional の連鎖の一部であり、= 演算子の右辺のコードのいずれも評価されていないことを意味します。前の例では、定数にアクセスするのは全く副作用がないため、その someAddress が全く評価されない事を確認するのは容易ではありませんでした。以下のリストは同じ代入を行いますが、それは address を作成する関数を使用しています。"関数が呼び出されました" と関数は、値を返す前に印刷し、= 演算子の右辺が評価されたかどうかを確認することができます。
何も印刷されないので、createAddress( ) 関数が呼び出されていないと言うことができます。
optional の値でメソッドを呼び出すために、またそのメソッドの呼び出しが成功したかどうかを確認するために、optional の連鎖を使用できます。そのメソッドが戻り値を定義していない場合でも、これを行うことができます。
Residence クラスでの printNumberOfRooms( ) メソッドは numberOfRooms の現在の値を出力します。ここでこのメソッドがどのように見えるかを示します:
このメソッドは、戻り値の型を指定していません。しかし、戻り値なしの関数 で説明したように、戻り値の型のない関数やメソッドは、Void 型の暗黙の戻り値の型を持っています。これは、それらが ( ) の値、または空のタプルを返すことを意味します。
Optional の連鎖での optional の値でこのメソッドを呼び出すと、optional の連鎖を通じて呼び出されたとき、戻り値は常に optional 型のものであるため、このメソッドの戻り値の型は、Void ではなく、Void? になります。このメソッドが、戻り値を自分で定義していなくても、printNumberOfRooms( ) メソッドを呼び出すことができたかどうかを確認するために if 文を使えるようになります。printNumberOfRooms 呼び出しからの戻り値と、nil を比較し、そのメソッド呼び出しが成功したかどうかを確認します。
optional の連鎖を通じてプロパティを設定しようとする場合も同様です。Optional の連鎖を使用したプロパティへのアクセス での上記の例は residence プロパティが nil であっても、john.residence の address 値を設定しようとします。optional の連鎖を通じてプロパティを設定しようとしても、プロパティが正常に設定されたかどうかを確認するため、nil に対して比較することができる Void? 型の値を返します。
Optional の連鎖を使用して、optional の値のサブスクリプトから値を取得し設定する事が可能で、そのサブスクリプトの呼び出しが成功したかどうかをチェックできます。
以下の例は、Residence クラスで定義されたサブスクリプトを使用して john.residence プロパティの rooms 配列内の最初の room の名前を取得しようとします。 john.residence は現在 nil のため、サブスクリプトの呼び出しは失敗します。
john.residence は optional の連鎖が試みられている optional の値であるため、このサブスクリプトの呼び出しでの optional の連鎖の疑問符は、サブスクリプトの括弧の前に、john.residence の直後に置かれています。
同様に、optional の連鎖でサブスクリプトを通じて新しい値を設定しようとすることができます。
residence は現在 nil のため、このサブスクリプトを設定しようとする試みも、失敗します。
その rooms の配列内の1つ以上の Room インスタンスと、john.residence に、実際の Residence インスタンスを作成し、代入する場合は、 Residence サブスクリプトを使用し、optional の連鎖を通じて rooms 配列内の実際のアイテムにアクセスできます。
サブスクリプトが、Swift の Dictionary 型のキーサブスクリプトのような、optional 型の値を返す場合、その optional の戻り値上に連鎖するためにはサブスクリプトの閉じ括弧の 後に 疑問符を書きます:
上記の例では、Int 値の配列に String キーをマッピングする2つのキー値のペアを含む testScores と言う dictionary を定義しています。この例では、"Dave" 配列の最初の項目を 91 に設定するため optional の連鎖を使用し、"Bev" 配列の最初の項目を 1 増分し、そして "Brian" のキーの配列内の最初の項目を設定しようとします。testScores の dictionary は "Dave" と "Bev" のキーを含んでいるため、最初の2つの呼び出しは、成功します。testScores の dictionary は "Brian" のキーを含んでいないため、第 3 の呼び出しは失敗します。
より深いモデル内のプロパティ、メソッド、およびサブスクリプトに貫き通して行くには、optional の連鎖の複数のレベルを一緒にリンクできます。しかし、optional の連鎖の複数のレベルは、返された値への optional 性のより深いレベルを追加しません。
それを別の言い方をすると:
そのため:
下記の例は、john の residence プロパティの address プロパティの street プロパティにアクセスしようとしています。ここで使われている optional の連鎖には 2つの レベルがあり、どちらも optional 型の、residence と address プロパティを通じて連鎖します。
john.residence の値は、現在有効な Residence のインスタンスを含んでいます。しかし、john.residence.address の値は、現在 nil です。このことから、john.residence?.address?.street への呼び出しは失敗します。
上記の例では、street プロパティの値を取得しようとしていることに注意してください。このプロパティの型は String? です。john.residence?.address?.street の戻り値は、そのため、optional の連鎖の2つのレベルが、プロパティの基本となる optional の型に加えて適用されているにもかかわらず、String? です。
john.residence.address の値として実際の Address インスタンスを設定し、address の street プロパティに実際の値を設定する場合は、複数レベルの optional の連鎖を通じて street プロパティの値にアクセスできます。
この例では、john.residence の値が現在有効な Residence インスタンスを含んでいるため、john.residence の address プロパティを設定しようとする試みは、成功します。
前の例では、optional の連鎖を通じて、optional の型のプロパティの値を取得する方法を示しました。また、optional の連鎖を使用して optional 型の値を返すメソッドを呼び出し、また必要に応じて、そのメソッドの戻り値上で連鎖できます。
以下の例は、optional の連鎖を通じて Address クラスの buildingIdentifier( ) メソッドを呼び出します。このメソッドは、String? 型の値を返します。前述したように、optional の連鎖をした後、このメソッド呼び出しの究極の戻り値の型も String? です。
このメソッドの戻り値で更なる optional の連鎖を実行したい場合は、メソッドの括弧の 後に、optional の連鎖の疑問符を書いて下さい。