背景
Swiftのオプショナルは、概念としてはおなじみになってきているような気がしますが、自分も含め使い方や勘違いをしている方もそこそこいる気がするので、とりまとめてみました。
そもそもSwiftにおけるnilとは?
- 空ポインタを意味するという意味ではない
- 空ポインタはポインタが言語仕様上あることを前提にして、ポインタが指し示す箇所がないという意味であるから。
- しかし、変数や定数、関数の返り値、式の評価結果がエラーなどで想定されているものと違う場合その値をnilで表現する。
オプショナル型とは?
- 通常の型だけでなく、特殊な場合にnilを値として持つことがある変数や式をオプショナル型という。
- 言い換えると、オプショナル型は特殊な場合にはnilが入ることを許容する型である。
オプショナル型の使い方
- 型宣言の後ろにクエスチョンマークをつける
- 例えば、Int型のオプショナル型は Int?
var b: Int?
オプショナルを学ぶ上で理解しにくいこと(その1)
- オプショナルとしての?はあくまで、宣言につける?のことである。実行時の?とは違う。またオプショナル(Optional)?とペアで使われるアンラップ(Forced unwrapping)!は実行時の!である。宣言時の!とは意味がことなる。今、どこの話をしているのかを頭のなかに、以下の表のマトリックスを意識すること。
具体的には以下のようなイメージである。
宣言時 | 実行時 | |
---|---|---|
? | Optional型 | Optional chaining |
! | Implicitly Unwrapped Optional型 | Forced unwrapping |
オプショナルを学ぶ上で理解しにくいこと(その2)
- とにかくこの界隈は横文字がたくさんでくるので、それがそもそも何を意味しているかを整理する必要があります。よく出てくるキーワードは、オプショナルのアンラップ方法として整理して理解するのがよさそうです。
オプショナルのアンラップの方法
- Forced unwrapping
- Optional chaining
- Optional binding
- 比較演算子 ※optional自体はアンラップせずに比較できるから結果をtrue,falseで取得
- Implicitly Unwrapped Optional型(有値オプショナル型)で宣言する ※そもそもアンラップしなくていいが、オプショナルの一種のアンラップ方法として
*/
そもそも、なぜオプショナル型では条件判定をするのか?
オプショナル型の変数を選択するということはnilが入ることを許容している。
だからこそ、nilが入った場合のエラーハンドリングのためにif文の条件判定が必要。
注意としては、条件判定自体には、オプショナル型は開示指定をする必要はない。
var nagano: Int? = Int("1998")
if nagano != nil {
print("nagano: \(nagano!)")
}
if-let文は何が便利なの?
上記のようにオプショナル型の変数を条件判定をしたうえでアンラップするのはたとえば、オプショナル型が複数ある場合は、書き方が冗長になりがち。なので、もっと簡易に同じことを実現するが用意されている。それが、if-let文。オプショナル束縛(optional binding)とも呼ばれる。
let year: Int? = Int("2020")
if let y = year {
print("あと\(y - 2016)年")
} else {
print("エラー")
}
複数個のオプショナル型の値を処理するときは特に便利
let olympic: String? = "tokyo"
if let y = year, o = olympic {
print("\(y) : \(o)")
}
その他知っておいて損はないこと
guard文
- guard文の特徴
- 条件節に記述した条件が成立しなかった場合にelse節が実行されて、コードブロックから抜ける。
また、条件節にオプショナル束縛を記述した場合、そこで利用した変数や定数をguard文の後続の文で利用可能。
- 条件節に記述した条件が成立しなかった場合にelse節が実行されて、コードブロックから抜ける。
let stock = [ "01", "2", "4", "05", "8", "3", "q", "X", "10"]
for str in stock {
guard let v = Int(str) else { //条件節が成立しない場合に実行される。if-let文だと条件節が成立する場合
print(str + "??")
break
}
print(v, terminator: " ")
}
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)
有値オプショナル型(implicitly optional型)
有値オプショナル型とは、暗黙的なオプショナル型は、空値(nil)をとり得るという意味では、
オプショナル型と同じですが、使用時にアンラップする必要がない。
let OlympicYear1: Int! = Int("2020") //宣言時に!を使う
print("あと\(OlympicYear1-2016)年") //アンラップの必要ない
よく見るケースとしては、IBOutletを設定した時の変数名が有値オプショナル型となっている。
これは、クラスがインスタンス化されたときには、IBOutletの値はnilが入っているが、その後
viewがloadされるタイミングで値が設定され、その後nilが入ることが通常ない場合ということ
なので有値オプショナル型がつかわれている。
@IBOutlet weak var myButton: UIButton!