記事の前提
・iPhoneプログラミングを学び始めてまだ1ヶ月程度のため、誤りがある可能性がございます。
・誤り、アドバイス等あればご連絡ください。
Optional型
swiftを勉強し始めると、様々な記事や参考書の中でこういったものを見かけます。
var str:String? = "Swift"
var str:String! = "Opational"
@IBOutlet weak var tableView: UITableView!
hoge.textLabel?.text = "test"
これまで見て見ぬふりをしてきたのですが、これを理解しなければ今後どこかで詰まると思い調べてみました。
初心者がOptional型を理解するためのポイント
- swiftでは何も意識せずに変数宣言すると、非Optional型となる
- 意図的に nilを代入したい場合以外は、オプショナル型を使わない
- "?"と"!"の違い
- Optional型変数をアンラップするための方法とその選び方
- nilを恐れること
Optional型と非Optional型
変数は通常のInt型やString型などとは別で、Optional型か非Optional型かという考えがあります。
Optional型 ・・・nilを代入できるデータ型
非Optional型・・・nilを代入できないデータ型
ようはnilを代入できるか出来ないかという違いになります。
またこれらはデータ型の一つのため、変数宣言時に使用します。
var str1:String = "Hello World" // 非Optional型
var str2:String! = "Hello World" // Optional型
このようにswiftでは、変数宣言時に"?"や"!"を何もつけなければ、非Optional型変数となり、そもそもnilの代入を許さない作りとなっています。
言い方を変えると、非Optional型変数であればnilでないことを保証していることがわかります。
(もしnilを代入しようとするとコンパイルエラーとなる)
参考
swiftに限らず、どの言語でもnilの入った変数にアクセスをしてしまうことを絶対に避ける必要があります。もしnilの変数にアクセスしてしまうと、アプリが落ちてしまうなど大きな不具合となってしまいます。
なぜOptional型が必要なのか
ではなぜOptional型が必要なのか?
通常の宣言でnilでないことを保証してくれているのであれば、わざわざOptional型変数を使って危険性を増す必要性があるのか?
何も値が代入されない可能性のある場合
例えばユーザー登録するシステムがあり、名前とメールアドレスは必須だがプロフィール画像は任意といった場合、プロフィール画像はユーザーが選択するまでは値が代入されない。そういった場合は、設定されるまでnilを代入しておく必要がある(らしい)
class User {
var name:string //必須
var mail:string //必須
var img_url:string? //任意
}
ラップとアンラップ
Optional型変数は、例えnilが入っていてもデータとしては取り扱うことができます。
しかしその名の通り、データをラップ(包む)しているためそのままでは通常の値も扱うことができません。
例えば以下の例のようにOptional型で宣言した値を、通常の非Optional型と同じように計算しようとするとエラーになってしまいます。
var num1: Int = 1
var num2: Int? = 2
print(num1 + num2) // error
このような場合にはアンラップ(包を外す)してあげる必要があります。
アンラップの種類
アンラップの方法について調べてみると、だいたい3つのことが書かれていましたのでそれらについて私なりの観点でまとめます。
まずアンラップする方法は大きく3つあります。
- Forced Unwrapping(強制的アンラップ)
- Optional Chaining (オプショナルチェイニング)
- Optional Binding(オプショナルバインディング)
方法 | 説明 | 安全性 | 簡易性 |
---|---|---|---|
Forced Unwrapping | Optional型を強制的にアンラップする | ✗ | ○ |
Optional Chaining | オプショナル型の変数に続くプロパティやメソッドを使う際に、オプショナル型変数がnilである場合は処理を止める | ○ | ○ |
Optional Binding | if文などの条件式を使ってnilの場合と処理を分ける | ○ | △ |
Forced Unwrapping(強制的アンラップ)
Forced Unwrapping(強制的アンラップ)は、オプショナル型を強制的にアンラップしてしまい、変数にどのような値が入っていても取り出す方法です。
var num: Int? = 100
print(num)
// => Optional(100) -> そのままでは値を使えない
print(num!)
// => 100 -> 値を取り出すことができる
強制的アンラップは"!"を末尾につけるだけで、アンラップすることができるため非常に簡単です。
しかし値が何であろうとアンラップするため、もし対象がnilだった場合はエラーでアプリが落ちてしまうため非常に危険です。
var num: Int?
print(num!)
// => nilアクセスによるエラー
そのため自分のローカル関数やクラスの中だけで使いnilでないことを保証できる場合を除き、別のモジュールからもらった値等の場合は使用するのを避けるべきかと思います。
Optional Chaining (オプショナルチェイニング)
Forced Unwrappingの場合は、強制的にアンラップしてしまうためnilアクセスしてしまう可能性がありました。そこでもしオプショナル型変数がnilだった場合に安全に動くようにするための方法の一つとしてOptional Chaining (オプショナルチェイニング)があります。
以下の例のようにテキストラベルに"?"をつけることで、もしインスタンスがnilだった場合はそのオプショナル型変数のプロパティ設定やメソッドの実行処理を中止します。
// textLabelがnilだった場合は"test"の代入を実行しない
textLabel?.text = "test"
Optional Binding(オプショナルバインディング)
Optional Bindingもif文等を使ってnilでないことを保証してからアンラップする手法となります。
var num: Int? = 10 // Optional
var total: Int = 0
if let check = num {
// nilでないことが保証できているためアンラップ
total = num! + 5
puts(total) // => 15
}else{
// nilの場合の処理
puts("str = nil")
}
こうすることで確実にnilでないことを保証できるため、安心してアンラップすることができます。
しかし毎回アンラップするためにif文を書くのは非常に面倒です。
そこで便利なNil Coalescing Operator(nil結合演算子)という文法を使うことで、同じことを簡単に記載することができます。
Nil Coalescing Operator(nil結合演算子)
Nil Coalescing Operator(nil結合演算子)はオプショナル型変数がnilだった場合は○○の値を代入するといった処理を1行で書くことができます。
例えば以下の例だと、まずnumがnilでないことを確認した上でアンラップしtotalに値を代入します。
var num: Int? = 10 // Optional
var total: Int = 0
if let check = num {
// nilでないことが保証できているためアンラップ
total = num!
}else{
// nilの場合は5を代入
total = 5
}
しかしNil Coalescing Operator(nil結合演算子)を使うことで以下のように書き換えることができます。
var num: Int? = 10 // Optional
var total: Int = 0
total = num ?? 5
numがnilでないなら、totalにnumを代入
numがnilなら、totalに5を代入
といった処理を簡単に書くことができます。
"!"と"?"の違い
よくネットで調べると"!"と"?"どちらで宣言したものもOptional型となる、といった記載があり使い分け方が分かりませんでした。
しかしよく調べてみると以下のような違いがありました。
"?" ・・・ いわゆる普通のOptional型
"!" ・・・ 暗黙的アンラップ型
?のオプショナル型はnilを代入できるが、そのままでは値を使用することができません
!のオプショナル型はnilを代入できるが、アンラップせずに値を使用することができます
種別 | ラップ状態 | nilを代入可否 |
---|---|---|
? | ラップされている | ○ |
! | ラップされていない | ○ |
!の場合はアンラップせずに値を使用することができます。しかしその分、強制的アンラップと同様にnilが入っている場合はアプリが落ちてしまいます。そのため慎重に選ぶ必要があります。
まとめ
- Optional型はswiftで安全な設計をするためには非常に重要な仕組みである
- Optional型は「nilアクセスをいかにしないか」の仕組みである
- アンラップする方法は基本的にはnilでないことを確認してから行い、nilだった場合の考慮もする必要がある