この記事は一部内容が古くなったので、全面的に書き直した記事を別にあげました(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型の変数をアンラップするには、以下の方法がある。
- Forced Unwrapping
- Optional Chaining
- Optional Binding
- 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]
(https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-XID_0)
[Facets of Swift, Part 1: Optionals]
(https://medium.com/@tammofreese/facets-of-swift-part-1-optionals-b8ba5b0051a2)
[stakoverflow - What does an exclamation mark mean in the Swift language?]
(http://stackoverflow.com/questions/24018327/what-does-an-exclamation-mark-mean-in-the-swift-language)
[先取り!Swift]
(http://dev.classmethod.jp/smartphone/iphone/forestalling-swift/)
[Wikipedia - Swift (プログラミング言語)]
(http://ja.wikipedia.org/wiki/Swift_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E))