Swift

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

More than 3 years have 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 (プログラミング言語)