SwiftのOptionalまわりについて
- Swiftの変数・定数はnilが発生しないようになっていて、nilを代入したい変数は、型をOptional型にする必要がある。
- String?のように型の後ろに?をつけるとnilを代入できるOptional型にWrapされる
let nums = [3, 4, 6] let lastNum = nums.last let ans = lastNum * 2 // Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?' ? print(ans)
lastNum
に数値ではなくnilが入ってる可能性があり、もしnilだった場合、lastNum * 2
が計算できずバグの要因になる可能性がある。したがって、Swiftはこういう場合、エラーを防ぐため、変数・定数にnilが代入されることを黙っては許可しない。
var num:Int num = 5 num = nil // Nil cannot be assigned to type 'Int'
nilを変数に代入しようとすると上記のようなエラーになる。
- var num:Int + var num:Int? num = 5 - num = nil // Nil cannot be assigned to type 'Int' + num = nil print(num) // nil
OptionalValue とは、nilの可能性がある値のこと。
上記のように、型の後ろに ?
を付けることで、numは、OptionalValue を代入できる Optional型の変数になり、num = nil
の式はエラーではなくなる。
var num:Int? num = 5 print(num) // Optional(5)
var num:Int? num = 5 print(num * 3) // Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?' ?
型に ?
を付けて宣言した変数にはnilを代入できることがわかったが、Optional 型の変数にnil以外の値を代入したときには注意が必要
上記の num
は、Int型の5が、Optional(Int)
のようにWrapされている状態。OptionalValueは、このままでは使えない。
var num:Int? num = 5 - print(num * 3) // Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?' ? + print(num! * 3) // 15
Optional(Int)
を計算式で使うには、Optional()
のラップを取り除く、つまりunwrapする必要がある。
unwrapの最も簡単な方法は、OptionalValue に !
をつけること。
var num:Int? num = nil print(num! * 3) // Fatal error: Unexpectedly found nil while unwrapping an Optional value
ただOptionalValueが本当にnilだった場合、強制unwrapするとエラーになる。
var num:Int? num = nil if num != nil { print(num! * 2) } else { print("num is empty") }
したがって、上記のようにnumがnilでないか確認する必要がある。 それをSwift側でやってくれるのが、Optional Binding。 Optional Binding とは、Optional Valueがnilでなければ値をunwrap して、一時変数に代入し、nilの場合はfalseを返す機能。(if, while, guard-else)
- if
var str:String? str = "Swift" if let msg = str { // Optional Valueがnilでなければ、値をunwrapして一時変数に代入 print(msg + "world") } else { // Optional Valueがnilのときは、else blockが実行される print("hello world") }
- if-where
let year1:String = "2001" let year2:String = "2016" if let startYear = Int(year1), let endYear = Int(year2), startYear < endYear { // year1, year2がnilだった場合、`startYear < endYear` の式は実行されない let years = endYear - startYear print("\(years)年間") // 15年間 }
- dictionary
var sum = 0 let dic:[String:Int?] = ["a": 24, "b": nil, "c": 10, "d": nil] for (_, value) in dic { if let num = value { sum+=num // dicから順に取り出したvalueがnilでないときだけ実行される } } print("数値の合計は\(sum)") // 34
- while
var str:String? = "★" var repeatString:String = "" var i = 0 while let stamp = str { // strがnilじゃないときunwrapしてstampに代入。nilならwhileループを実行しない。 repeatString += stamp i += 1 if(i >= 10) { break } } print(repeatString) // ★★★★★★★★★★
- guard-else
let who = "さくら" var level:Int? func hello() { guard let theLevel = level else { print("level is nil") return } if theLevel<10 { print(who+"隊員") } else { print(who+"上級隊員") } } hello() // level is nil levelがnilなので、処理が中断されている level = 5 hello() // サクラ隊員
print(count) // nil print(count ?? 2) // 2 price = (count ?? 2) * 250 print(price) // 500 count = 3 print(count) // Optional(3) print(count ?? 2) // 3 price = (count ?? 2) * 250 print(price) // 750
OptionalValueがnilのとき、??
を使うとnilに代わる値を指定できる
var balls:[(size:Int, color:String)] =[] - var ballSize = balls.first.size // Value of optional type '(size:Int, color:String)?' not unwrapped; did you mean to use '!' or '?' ? + var ballSize = balls.first?.size print(ballSize) // nil
var balls:[(size:Int, color:String)] =[(size:2, color:"red"), (size:4, color:"green") ] - var ballSize = balls.first.size // Value of optional type '(size:Int, color:String)?' not unwrapped; did you mean to use '!' or '?' ? + var ballSize = balls.first?.size print(ballSize) // Optional(2)
.
でアクセスするときに対象に?
をつけることで実行時エラーを回避できる記述方法をOptional chainと呼ぶ
上記だと、Optional型で、値を使うことができず、unwrapする必要があるので、Optional Bindingと組み合わせる。
var balls:[(size:Int, color:String)] =[(size:2, color:"red"), (size:4, color:"green") ] var ballSize = balls.first?.size + if let ballSize = balls.first?.size { + print(ballSize) // 2 + }