Swiftでは、値を持たない定数・変数にアクセスしようとするとエラーになる。
値を持たない状況とは、アプリで名前を入力するテキストフィールドにユーザーが何も入力せずに次の処理に進んだ場合などが想定される。
このような場合に備え、値を持たない(入力されていない・算出できない)状態に対応するためオプショナル型として定数・変数を宣言しておく必要がある。
オプショナルが使用される例として、String型 から Int型 へ型変換 がある。
Int(変数名) とすることで、変数に格納されていた値はInt型に変換される。
この時格納されていた値が "123" など、数値型が対応できる値であれば問題ないが、"abc" といった数値に変更しようがない値が格納されていた場合、型変換の結果は何も返されない。
let stringNumber = "123"
let convertedFromStringToInt = Int(stringNumber)
// 型変換は成功し、convertedFromStringToInt には Int型整数、123が格納される。
let stringABC = "abc"
let convertWillBeFailed = Int(stringABC)
// abc に相当する数値がないため型変換は失敗し、convertWillBeFailed は値が無い状態、 nil になる。
上記のように、型変換に失敗する可能性があるため戻り値は通常のInt型ではなく、 オプショナルInt が返される。
convertedFromStringToIntの型は Int? となり、値があるかもしれないし無いかもしれないオプショナルを示唆するようになる。
nil
? を添えた型名、Int? や String? の定数・変数には nil を格納して値が存在しないことを表現できる。
var serverResponseCode: Int? = 404
// 変数 serverResponseCode は Int型 であり、かつ値が存在しない状況にも対応できる。宣言時の初期値は 404。
serverResponseCode = nil
// serverResponseCode に nil を格納することで、変数に値が存在しないことを意味する
nil を使うには、必ずその定数・変数は ? が添えられたオプショナル型を宣言時に持っていなければいけない。
var nilVar: String? = "nilVar can have nil"
nilVar = nil
// String? 型なので nil を持てる。
var nonNilVar: String = "This can't have nil"
nonNilVar = nil
// 宣言時に ? が添えられていないので、nil を持てずエラーになる。
オプショナル型を指定して初期値を持たせず宣言した場合、初期値は自動で nil になる。
var surveyAnswer: String?
// surveyAnswer に自動で nil が割り当てられる。
オプショナル型定数・変数にアクセスする
オプショナル型で宣言された定数・変数にアクセスする際にはいくつかの手順(条件判定したり記号を添えたり)を踏む必要がある。
if文と強制アンラップ
まず、ここでのラップ(wrap)とは、「包む」という意味。アンラップで包みを開ける、という意味。英語だと包まれてるイメージなのか?
オプショナルにアクセスする方法の一つとして、if文を使って値があるならばオプショナルの値にアクセスする、値が無い(nil)
ならばアクセスしない、という方法がある。
この条件には 等価演算子(==) または 非等価演算子(!=) を使う。
例: もし convertedNumber が nil でなければ(値があれば)、その値をプリントする
let stringNumber = "123"
let convertedNumber = Int(stringNumber)
if convertedNumber != nil {
print("convertedNumber is \(convertedNumber!)")
}
// nil では無いので "convertedNumber is 123" がプリントされる。
上記では、if文の条件チェックによって convertedNumber が nil でない確認が行えている。
値にアクセスする際対象のオプショナルに ! を添えて値にアクセスしている。
オプショナル定数・変数に ! を添えて値にアクセスすることを 強制アンラップ という。
! を添えることは、「その定数・変数の値有無に関わらず値にアクセスしろ」と強制することを意味する。
! を添えると、Swiftはその定数・変数の値にアクセスしようとする。もし値が存在しない場合、その処理はエラーになってしまうので必ず if で値の存在を確認する仕組みを作る必要がある。
if 文についての詳細は Control Flow を参照すること。
オプショナルバインディング
バインディングとは、「まとめる」という意味。
オプショナルの値有無を確認し、もし有るならば仮の定数・変数に格納して、その仮の定数・変数にアクセスして値を利用する、という手法をオプショナルバインディングという。
オプショナルバインディングの基本文法は下記
if let 仮の定数 = オプショナル {
処理文
}
使用例
let stringNumber = "123"
if let tempConstant = Int(stringNumber) {
print("stringNumber has a value of \(tempConstant)")
} else {
print("stringNumber could not be converted to an integer")
}
// "stringNumber has a value of 123" とプリント。
Int(stringNumber)の結果がもし tempConstant に格納される場合、if文の条件判断は 真 になるため"stringNumber has a value of 123"がプリントされる。
また、オプショナルバインディングで値格納に使われた定数はオプショナルではなく、通常の型(上記ではInt型)になるので、
! を添えずにアクセスできる。
もし、stringNumber が "abc" など Int型へ変換できない値を持っている場合、if文結果は 偽 になるため、"stringNumber could not be converted to an integer"がプリントされる。
暗黙的アンラップオプショナル
プログラムの構造上、そのオプショナルが 常に 値を持っていることがわかりきっている場合、値の有無チェックを省略してオプショナルにアクセスすることができる。これを暗黙的アンラップオプショナルと呼ぶ。
暗黙的アンラップオプショナルとして利用するには、オプショナル宣言時に ? ではなく ! を添える。
暗黙的アンラップオプショナルはオプショナル宣言時に値が格納されること、またその後も継続的に値が格納され続ける場合に有用で、これの主な利用はクラスの初期化時で、これについての詳細は Unowned References and Implicitly Unwrapped Optional Properties を参照すること。
暗黙的アンラップオプショナルは実際のところ通常のオプショナルであり、また非オプショナルの値として、アンラップの手順なしでアクセスできる。
使用例:
let possibleString: String? = "An optional String"
let forcedString: String = possibleString!
// ! を添えることが必須。
let assumedString: String! = "An implicitly unwrapped optional string"
let implicitString: String = assumedString
// ! は不要になる
暗黙的アンラップオプショナルはアクセスされるたびに自動的にアンラップされる許可を与えているようなものと考えて良い。
値が存在し続けることを前提に、毎回定数・変数名に ! を添えるよりも、そのオプショナル宣言時の型名に ! を添えることでアンラップの手間を省けるようになる。
暗黙的アンラップオプショナルだったとしても、やはり値が nil の場合、
やはりプログラムはエラーとなることに注意すること。
また、暗黙的アンラップオプショナルとして宣言して利用開始していても、普通のオプショナルと同じくチェック処理を行うこともできる。
if assumedString != nil {
print(assumedString)
}
// "An implicitly unwrapped optional string" がプリントされる。
暗黙的アンラップオプショナルをオプショナルバインディングすることもできる。
if let definiteString = assumedString {
print(definiteString)
}
// "An implicitly unwrapped optional string" がプリントされる。
もし値が nil になる可能性が少しでも有る場合は暗黙的アンラップオプショナルは使わないこと!
可能性がある場合は常に普通のオプショナルとして宣言し、チェックの手順を踏むこと!