前回に引き続き、Optional(オプショナル)の続きになります。
今回は「オプショナルバインディング」と「オプショナルチェインニング」についてです。
オプショナルバインディング
オプショナルバリューがnilでなければ、アンラップして変数に代入する。
nilであればfalseを返す機能のことです。
これはif文とwhile文の分岐で利用できます。
if let 変数 = オプショナルバリュー {
// 処理
}
while (let 変数 = オプショナルバリュー) {
// 処理
}
わざわざnilチェックを単独で書く必要がないため、かなり便利です。
var staff:String?
if let s = staff { // staffがnilなのでfalse
println("本日の担当者は\(s)です。")
} else {
println("本日は担当者なし")
}
本日の担当者はなし
おまけ
また、以前は複数条件の場合に下記のように書く必要があり、
nil
チェックが冗長になっていました。
if staff != nil && guest != nil {
} else {
}
swift1.2からは
下記のようにすっきりと書くことができるようになりました。
if let s = staff, g = guest {
} else {
}
オプショナルチェインニング
オブジェクトのプロパティ、メソッド等にアクセスする際に、
それがnilだった場合にエラーが起きてしまいます。
それを防ぐためにプロパティ名やメソッド名に?
をつける記述方法のことを
オプショナルチェインニングといいます。
今回はSoldierクラスとWeaponクラスというクラスを使って説明します。
class Soldier {
var weapon:Weapon? // オプショナル
}
class Weapon {
var name:String = "Excalibur"
}
Soldierクラスのインスタンスを作成して
Weapon.nameにアクセスしてみます。
var soldier = Soldier() // soldier.weapon = nil
println(soldier.weapon!.name) // weaponがnilなのでnameにアクセスした段階でエラー。
Soldierクラスのweaponプロパティはオプショナルバリューなので、
初期値のnilが入っています。
nilであるweaponのnameプロパティにアクセスしたためエラーが起きます。
var soldier = Soldier() // soldier.weapon = nil
println(soldier.weapon?.name) // nil
weapon?
にしたことで
weaponがnilであることを確認した時点で処理が終わり、
エラーになることなくnil
を返してくれます。
メソッドの戻りがオプショナルバリューの場合
次に、メソッドの戻り値がオプショナルバリューの場合を見てみます。
use()はItemを返します。
そのItemのnameプロパティにアクセスするケースで考えてみます。
下のuse()!.name as String!
のようにすると、
use()の戻り値がnilの場合、下記のようにエラーが発生します。
// 5回ループ
for _ in 0...5 {
println("手持ちの道具:\(use()!.name as String!)")
}
struct Item {
var name:String
}
var item:Item = Item(name: "たいまつ")
var useCount:Int = 3 // 使用回数
func use() -> Item? {
if(useCount == 0){
print("\(self.item.name)を持っていない。")
return nil
} else {
useCount--
print("\(self.item.name)を使った。周辺が明るくなった。")
if(useCount == 0) {
print("\(self.item.name)が燃え尽きた。")
}
return self.item
}
}
たいまつを使った。周辺が明るくなった。手持ちの道具:たいまつ
たいまつを使った。周辺が明るくなった。手持ちの道具:たいまつ
たいまつを使った。周辺が明るくなった。たいまつが燃え尽きた。手持ちの道具:たいまつ
たいまつを持っていない。fatal error: unexpectedly found nil while unwrapping an Optional value
そこでプロパティと同じように
use()
の後ろに?
をつけます。
ここではuse()?.name as String!
のようにすると、
エラーにならず、use()?.name
はnil
が返ってきます。
// 5回ループ
for _ in 0...5 {
println("手持ちの道具:\(use()?.name as String!)")
}
struct Item {
var name:String
}
var item:Item = Item(name: "たいまつ")
var useCount:Int = 3 // 使用回数
func use() -> Item? {
if(useCount == 0){
print("\(self.item.name)を持っていない。")
return nil
} else {
useCount--
print("\(self.item.name)を使った。周辺が明るくなった。")
if(useCount == 0) {
print("\(self.item.name)が燃え尽きた。")
}
return self.item
}
}
たいまつを使った。周辺が明るくなった。手持ちの道具:たいまつ
たいまつを使った。周辺が明るくなった。手持ちの道具:たいまつ
たいまつを使った。周辺が明るくなった。たいまつが燃え尽きた。手持ちの道具:たいまつ
たいまつを持っていない。手持ちの道具:nil
たいまつを持っていない。手持ちの道具:nil
たいまつを持っていない。手持ちの道具:nil
「オプショナルバインディング」、「オプショナルチェインニング」をうまく使うことで
nilのエラーで落ちる、ということがかなり減ると思います!
Swiftではオプショナルをしっかり理解して実装していくことが
かなり重要だと思うので、しっかり身につけたいところです。。。