Swiftで「ええやん!」と興奮しつつも「なんでエラーなん?」と頭を悩まされ続けているOptional型。
この場合はOptinal型でこう省略して書けるよ!的な記事を見ては忘れてと繰り返しているので一気に理解を深めておきたいと思っています。
型宣言とアンラップ
Optional型の書き方として「!」と「?」が頻出します
宣言時の「!?」とアンラップ時の「!?」は意味合いが違います
ここが混乱のポイントだと思います
型宣言における「!」と「?」
Optional型宣言の省略系となる
// 以下は同義
var str :String? = "string"
var str :Optional<String> = "string"
// 以下は同義
var str :String! = "string"
var str :ImplicitlyUnwrappedOptional<String> = "string"
if ([self.delegate responsToSelector:@selector(aaa)]) {
[self.delegate aaa]
}
self.delegate?.aaa?()
Optional<型>
とImplicitlyUnwrappedOptional<型>
はnilを代入することができる型です。(非Optional型ではエラー)
これらは元の型とは異なります。なので元の型の持っているfuncなどにアクセスすることはできません。
アクセスと同様にも元の型への代入もできません。型エラーです。
そのため、元の方にアンラップすることでfuncなどへのアクセスします。
Optional<型>
とImplicitlyUnwrappedOptional<型>
の違い
Optional<型>
は「nilが代入できる型」
ImplicitlyUnwrappedOptional<型>
は加えて「使用時にForced Unwrappingされる型」
アンラップにおける「!」と「?」
変数をアンラップする方法として4つある。
「!」はForced Unwrappingする。
「?」はOptional Chainingする。
- Forced Unwrapping
- Optional Chaining
- Optional Binding
- ImplicitlyUnwrappedOptionalで宣言する
Forced Unwrapping
var str :String? = "string"
var unwrapped :String = str!
「!」を使う
Optional型を元の方に戻す
Optional型にnilが入っているとエラー
nilだとエラー
Optional Chaining
var str :String? = nil
str?.lowercaseString
「?」を使う、少し癖が有る
Optional型を返しそれをchainしていく、nilの場合はnilを返す
アンラップといえどOptional型を返すとはどういうことかというと
// arr自体もOptional型で値もOptional型
var arr :Array<String?>? = [nil, "two"]
// arrを「?」でOptional型をアンラップして、Forced Unrappingで元の値へ変換する
arr?[1]!.daikobay // -> が結果はOptional型となる
2行目のアンラップ処理を詳しく言うと
* Optionalを「?」でアンラップ、nilでないので元の型のArrayとして扱う
* Arrayとして扱うので変数へアクセスできるが、次の値がnilの可能性もあるのでOptional型としてChainingしていく
* 中のStringはOptionalなので「!」でForced Unwrappingし元の型へ変換
* しかし、arrを「?」しOptional Chainingしているので元の型からOptional型へ変換される
とこんな感じでOptional Chainingはnilでもエラーとしないので元の型へ変換してアクセス可能にした後、Optional型へ戻す
nilを許容する
Optional Binding
var str :String? = "string"
if var s :String = str {
println(s)
}
とnilでなくアンラップ可能ならif内部へ進む
nilでなければ処理する
問題集
ということで問題集でも用意します。
aa
aa
playgroundは優しいので即座にエラーメッセージを出してくれます。
問題集を使う場合はplaygroundを使わないことをお勧めします。
question-1
// 以下を正しいコードに直そう
func getAny () -> AnyObject {
var any :NSString? = "NSString"
return any
}
answer-1
// 返り値はOptional型じゃないので
func getAny () -> AnyObject {
var any :NSString? = "NSString"
return any!
}
// もしくは
func getAny () -> AnyObject? {
var any :NSString? = "NSString"
return any
}
question-2
// 2行目はOptional型?非Optional型?
var arr :Array<String?>? = ["one", "two"]
arr?[0]!
answer-2
Optional型
「!」がついているが、先に「?」でOptional Chainingとなっているので「!」がついていてもOptional型に変換される
例えば
(arr?[0])!
としていれば最後に「!」で元の型に戻るので非Optional型になる
question-3
// varやfuncの宣言はいじらず"I'm bob"と出力されるように修正しよう
// このままでは'Optional("I\'m Optional(\"bob\")")'と出力される
class Person
{
var name :String?
init (name: String)
{
self.name = name
}
func say () -> String? {
return "I'm \(self.name)"
}
}
var bob :Person? = Person(name: "bob")
println(bob?.say()!)
answer-3
sayを以下のように実装
if var greet :String = self.name {
return "I'm (greet)"
}
return nil
sayの呼び出し方を以下のように変更
println((bob?.say())!)