[Swift] Optional型についてのまとめ

  • 258
    Like
  • 0
    Comment
More than 1 year has passed since last update.

この記事は一部内容が古くなったので、全面的に書き直した記事を別にあげました(14/10/17 追記)。

[Swift] Optional 型についてのまとめ Ver2

Optional型とは?

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

Optional型の宣言方法

var a: Int? // Optional型
var b: Int  // 非Optional型

var c: String? // Optional型
var d: String  // 非Optional型

T?は、Optional<T>のシンタックスシュガーである

var a: Int?
var b: Optional<Int> // Int?と同じ意味

Optional型はnilが代入されるのを許す

var a: Int? // Optional型
a = nil     // コンパイラエラーは発生しない

var b: Int // 非Optional型
b = nil    // コンパイラエラーが発生する: Could not find an overload for '__conversion' that accepts the supplied arguments

Optional型の初期値はnil

Optional型の初期値はnilである。

var a: Int? // Optional型
println(a)  // -> nil

非Optional型の場合、初期値には何も入っていない(nilも入っていない)。

var b: Int // 非Optional型
println(b) // コンパイラエラーが発生する: Variable 'b' used before being initialized

Optional型は、非Optional型と同じようには扱えない

var a: Int = 1
println(a + 2) // -> 3

var b: Int? = 1
println(b + 2) // コンパイラエラーが発生する: Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?

上記のとおり、Int?は、Intとは別の型なので、同じように操作することはできない。Intと同じように扱うためには、変数を「アンラップ」する必要がある(後述)。

ラップ(wrap)とアンラップ(unwrap)

「ラップされている」とは?

Optional型(Optional<T>型)の変数のことを「ラップされている」と言う(おそらく、Optional<T>型にT型の変数が包まれていることに由来すると思われる)

「アンラップする」とは?

Optional型(Optional<T>型)からT型の変数を取り出すことを、「アンラップする」と言う。

Optional型の変数をアンラップする方法

Optional型の変数をアンラップするには、以下の方法がある。

  1. Forced Unwrapping
  2. Optional Chaining
  3. Optional Binding
  4. Implicitly Unwrapped Optional型

1. Forced Unwrapping

以下のように、!を使って、Optional型の変数をアンラップすることができる。

class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var d: Dog? = Dog() // Optional型

// ラップされた状態のままでは、barkメソッドを呼び出すことができない
println(d.bark()) // コンパイラエラーが発生する: 'Dog?' does not have a member named 'bark'

// 変数dのうしろに「!」を付けることで、アンラップすることができる(Forced Unwrapping)
println(d!.bark()) // -> Wan!

// 変数dの中身がnilだった場合は、「!」でアンラップする際にランタイムエラーが発生する
d = nil
println(d!.bark()) // ランタイムエラーが発生する: fatal error: Can't unwrap Optional.None

2. Optional Chaining

以下のように、!ではなく?を使ってアンラップすることを「Optional Chaining」と言う。

class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var d: Dog? = Dog() // Optional型

// 変数dのうしろに、「?」を付けるとアンラップされる(Optional Chaining)
println(d?.bark()) // -> Wan!

// 変数dにnilが入っていた場合、アンラップ時にnilが返ってくる
d = nil
println(d?.bark()) // -> nil

Forced Unwrapping(!を使う方法)と違い、nilに対してアンラップをしても、ランタイムエラーは発生しない。代わりに、nilが返ってくる。

注意:println について(14/10/17 追記)

println(d?.bark()) の出力結果が、最新版の xcode と異なるので、注意してください。

  • Xcode 6 Beta の場合(この記事の執筆に使ったバージョン)
println(d?.bark()) // -> Wan!
  • Xcode 6.1 GM seed 2 の場合
println(d?.bark()) // -> Optional(Wan!)

println で optional 型の変数を出力するときに、Optional() という文字列が付加されるようになりました。

3. Optional Binding

if文やwhile文の条件式で宣言され、Optional型の変数を代入された変数は、非Optional型になる。これを「Optional Binding」と言う。

class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var wrappedDog: Dog? = Dog() // Optional型

// if文の条件式で宣言された変数は、非Optional型になる(Optional Binding)
if var unwrappedDog1 = wrappedDog {
    // 「!」を付ける必要はない
    println(unwrappedDog1.bark()) // -> Wan!
}

// 「var」と「let」のどちらで宣言してもかまわない
if let unwrappedDog2 = wrappedDog {
    println(unwrappedDog2.bark()) // -> Wan!
}

// while文でもOptional Bindingが利用できる
while var unwrappedDog3 = wrappedDog {
    println(unwrappedDog3.bark()) // -> Wan!
    break
}


//////////
// 補足1 //
//////////

//
// 上記の変数「unwrappedDog1」「unwrappedDog2」「unwrapedDog3」は、ブロック内でのみ有効
//
println(unwrappedDog1.bark()) // コンパイラエラーが発生する: Use of unresolved identifier 'unwrappedDog1'
println(unwrappedDog2.bark()) // コンパイラエラーが発生する: Use of unresolved identifier 'unwrappedDog2'
println(unwrappedDog3.bark()) // コンパイラエラーが発生する: Use of unresolved identifier 'unwrappedDog3'


//////////
// 補足2 //
//////////

//
// wrappedDog変数がnilの場合は、if文の条件式の結果がfalseになるので、ブロック内の処理は実行されない。
//
wrappedDog = nil
if var unwrappedDog4 = wrappedDog {     // falseになる
    println("This is not printed out.") // -> 出力されない
}

4. Implicitly Unwrapped Optional型

Optional(Optional<T>)型ではなく、Implicitly Unwrapped Optional型を使うと、暗黙的にアンラップすることができる。変数をImplicitly Unwrapped Optional型で宣言する際には、?ではなく、!を使う。

class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

// 「?」ではなく、「!」を使って宣言する
var d: Dog! = Dog() // Implicitly Unwrapped Optional型

// アンラップ操作は不要(暗黙的にアンラップされる)
println(d.bark()) // -> Wan!

// 変数dの中身がnilだった場合、暗黙的なアンラップ時にランタイムエラーが発生する
d = nil
println(d.bark()) // ランタイムエラーが発生する: fatal error: Can't unwrap Optional.None   

T!は、ImplicitlyUnwrappedOptional<T>のシンタックスシュガーである

var a: Int! // Implicitly Unwrapped Optional型
var b: ImplicitlyUnwrappedOptional<Int> // Int!と同じ意味

Optional型とImplicitly Unwrapped Optional型との違い(まとめ)

宣言方法 型の実体 アンラップ時の操作
Optional型 var a: T? Optional<T> 明示的な操作が必要
Implicitly Unwrapped Optional型 var a: T! ImplicitlyUnwrappedOptional<T> 不要(暗黙的にアンラップされる

nilに対して、アンラップをした場合の挙動(まとめ)

Optional(Optional<T>)型の場合

アンラップの方法 結果
Forced Unwrappingの場合(!を使う方法) ランタイムエラーが発生する
Optional Chainingの場合(?を使う方法) nilが返ってくる
class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var d: Dog? = nil // Optional型

// Forced Unwrappingの場合(!を使う場合)
println(d!.bark()) // ランタイムエラーが発生する: fatal error: Can't unwrap Optional.None

// Optional Chainingの場合(?を使う場合)
println(d?.bark()) // -> nil

Implicitly Unwrapped Optional型(ImplicitlyUnwrappedOptional<T>)型の場合

アンラップの方法 結果
暗黙的にアンラップされる ランタイムエラーが発生する
class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var d: Dog! = nil // Implicitly Unwrapped Optional型
println(d.bark()) // ランタイムエラーが発生する: fatal error: Can't unwrap Optional.None   

開発環境

  • Xcode 6 Beta
  • Mac OS X 10.9.3

実行環境

  • iOS Simulator (iPhone5s / iOS 8.0)

参考文献

The Swift Programming Language

Facets of Swift, Part 1: Optionals

stakoverflow - What does an exclamation mark mean in the Swift language?

先取り!Swift

Wikipedia - Swift (プログラミング言語)