SwiftのOptional型を勉強してみた

Optional型とは

  • Optional型 : nilが入るのを許す
  • 非Optional型 : nilが入るのを許さない

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\型)からT型の変数を取り出すことを、「アンラップする」と言う。

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ブロックを抜けると条件式で代入した変数は使えなくなる。

guard文の使い方

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です")
}

応用編

SwiftのOptional型を極める