SwiftのOptional型を勉強してみた
Optional型とは
Optional型の宣言方法
var a: Int? // Optional型 var b: Int // 非Optional型 var c: String! // Optional型 var d: String // 非Optional型
Optional型とImplicitly Unwrapped Optional型
型 | 宣言方法 | 型の実体 | アンラップ時の操作 |
---|---|---|---|
Optional型 | var a: T? | Optional\ |
明示的な操作が必要 |
Implicitly Unwrapped Optional型 | var a: T! | ImplicitlyUnwrappedOptional\ |
不要(暗黙的にアンラップされる |
ラップ(wrap)とアンラップ(unwrap)
Optional型(Optional\
Optional型(Optional\
Optional型の変数をアンラップする方法
1. Forced Unwrapping(!使用)
以下のように、!を使ってアンラップすることを「Forced Unwrapping」と言う。
class Dog { func bark() -> String { return "Wan!" } } var d: Dog? = Dog() // Optional型 print(d!.bark()) // -> Wan!
2. Optional Chaining(?使用)
以下のように、!ではなく?を使ってアンラップすることを「Optional Chaining」と言う。
class Dog { func bark() -> String { return "Wan!" } } var d: Dog? = Dog() // Optional型 print(d?.bark()) // -> Wan!
3. Optional Binding(if let, guard let使用)
if文やguard文の条件式で宣言され、Optional型の変数を代入された変数は、非Optional型になる。これを「Optional Binding」と言う。
class Dog { func bark() -> String { return "Wan!" } } var d: Dog? = Dog() // Optional型 // if letでアンラップ if let unwrappedDog2 = wrappedDog { print(unwrappedDog2.bark()) // -> Wan! } // guard letでアンラップ guard let unwrappedDog2 = wrappedDog else{ return } print(unwrappedDog2.bark()) // -> Wan! }
<参考>guard文を使用する目的
- 条件に合わない場合に処理を抜けるコードをシンプルに記述することができる。
- guard文ではelseブロックを抜けたあとでも変数を利用できる。
if文の場合は、if-elseブロックを抜けると条件式で代入した変数は使えなくなる。
4. Implicitly Unwrapped Optional型(暗黙的アンラップ)
Optional型ではなく、Implicitly Unwrapped Optional型を使うと、暗黙的にアンラップすることができる。変数をImplicitly Unwrapped Optional型で宣言する際には、?ではなく、!を使う。
class Dog { func bark() -> String { return "Wan!" } } // 「?」ではなく、「!」を使って宣言する var d: Dog! = Dog() // Implicitly Unwrapped Optional型 // アンラップ操作は不要(暗黙的にアンラップされる) print(d.bark()) // -> Wan!
nilに対して、アンラップをした場合の挙動
Optional型の場合
アンラップの方法 | 結果 |
---|---|
Forced Unwrappingの場合(!を使う方法) | ランタイムエラーが発生する |
Optional Chainingの場合(?を使う方法) | nilが返ってくる |
class Dog { func bark() -> String { return "Wan!" } } var d: Dog? = nil // Optional型 // Forced Unwrappingの場合(!を使う場合) print(d!.bark()) // ランタイムエラーが発生する: fatal error: Can't unwrap Optional.None // Optional Chainingの場合(?を使う場合) print(d?.bark()) // -> nil
Implicitly Unwrapped Optional型の場合
アンラップの方法 | 結果 |
---|---|
暗黙的にアンラップされる | ランタイムエラーが発生する |
class Dog { func bark() -> String { return "Wan!" } } var d: Dog! = nil // Implicitly Unwrapped Optional型 print(d.bark()) // ランタイムエラーが発生する: fatal error: Can't unwrap Optional.None
演算子を用いたアンラップ
nil結合演算子
??という演算子を使うと、オプショナル型がnilでなければその値を、nilの場合は指定した値を与えることができる。
var a: Int? let b = a ?? 10 // aはnilなので、10が代入される var s: String? = "Hello" println(s ?? "Bye") // sはnilでないので、その値(Hello)が出力される
nil合体演算子
以下のように、通常のif-let文や三項演算子より手軽に記述することができる。
通常のif-let文
var opv: String? = nil let S = "なにか" if let o = opv { print(o) } else { print(S) }
三項演算子
print((opv != nil) ? opv! : S)
nil合体演算子
print(opv ?? S)
nil合体演算子2つ以上に重ねることができる
let x: Int? = nil, y: Int? = nil, z: Int? = 3 let val = x ?? y ?? z ?? 0 print(val)
ハマったポイント
データクラス
//Infoクラス class Info { var price:Int? var reqDnb:RegDnb? } //Infoに含まれる電文クラス class RegDnb { var cardNum:Int? }
失敗例
データのクラスにnilが入っていたら、データに値を入れてもnilとなってしまう。
let info = Info() //info.reqDnb.cardNum = 12 // -> reqDnbをアンラップしろと怒られる info.reqDnb?.cardNum = 12 print(info.reqDnb?.cardNum) // -> nil(理由:reqDnbがnilとなるから) //アンラップ処理 if let unwrapCardNum = info.reqDnb?.cardNum { print(unwrapCardNum) } else { print("nilです") // -> nilです }
成功例
直属のデータクラスをインスタンス化し、値を挿入した。
let reqDnb = RegDnb() // -> ReqDnb reqDnb.cardNum = 12 print(reqDnb.cardNum) // -> Optional(12) //アンラップ処理 if let unwrapCardNum = reqDnb.cardNum { print(unwrapCardNum) // -> 12 } else { print("nilです") }
応用編
- map、クロージャなどを使用した記載