【Swift】Optional型

Optional型( オプショナル型 )とは、nilの可能性があるときに使う型です。
Swiftでは値が無い状態のことを、nilと言います。
例えば、必須項目や非必須項目をユーザーに入力してもらう時など、入力されるか分からない項目がある場合などに Optional型を使います。

optionalアイキャッチimg

前回は タプル について書きました。

今回は Optional型 について書いていこうと思います。

Optional型とは

Optional型( オプショナル型 )とは、nil の可能性があるときに使う型です。
Swift では 値が無い 状態のことを、nil と言います。
逆に言うと、Optional型 で宣言している場合のみ nil を代入する事ができます。
例えば、必須項目 や 非必須項目 をユーザーに入力してもらう時など、入力されるか分からない項目がある場合などに Optional型 を使います。

Optional型の書式

//書式1
型名? 

//書式2
Optional<型名>

型名を書いた後に ? を付けます。( 書式 1 )
または
Optional と書いた後 < > の中に型名を書きます。( 書式 2 )

Optional型 の書き方

//例1
var a:Int?
print(type(of: a)) //Optional<Int>

//例2
var b:Optional<Int>
print(type(of: b)) //Optional<Int>

変数 abOptional型 で宣言しています。
この 2 つの変数の型を見てみると、Optional<Int> となっています。これは Optional Int型 という型で、ただの Int型とは別の型になります。なので原則同じ型でなければ演算できないので、Optional Int型Int型 は演算できません。
Optional型 の値を使いたい時は、アンラップ という処理が必要になります。

Int型 に限らず、他の型でも頭に Optional と付いている型の値を使いたい時はアンラップが必要です。

※ 例 1 は初期値を入れずに宣言するとデフォルトで nil が代入されますが、
例 2 は初期値を入れずに宣言しても nil が代入されません。例 2 はどこかで値を代入しないと使うことができないので注意。

//例1
var a:Int?
print(type(of: a)) //Optional<Int>
print(a) //nil

//例2
var b:Optional<Int> = nil //初期値としてnilを代入
print(type(of: b)) //Optional<Int>
print(b) //nil

Optional型を使った例

必須項目と非必須項目がある場合の例

//必須項目と非必須項目がある場合の例
let name:String //必須項目
var mail:String //必須項目

//Optional型で非必須項目の宣言
var phone:String? //非必須項目
var birthday:String? //非必須項目

//必須項目に値を代入
name = "hoge"
mail = "aaa@123.com"

//アクセス
print("\(name)のアカウント情報") //hogeのアカウント情報
print("アカウント名 \(name)") //アカウント名 hoge
print("メールアドレス \(mail)") //メールアドレス aaa@123.com

//非必須項目にアクセス
print("電話番号 \(phone)") //電話番号 nil
print("誕生日 \(birthday)") //誕生日 nil

必須項目と非必須項目に使う変数・定数を宣言して、必須項目にだけ値を代入し全ての変数・定数にアクセスしている例です。
必須項目には値を代入しているのでこれまで通りアクセスできて値もきちんと反映されています。
次に非必須項目ですが、 値を代入していません。
Optional型 で宣言しているので値の代入がされてなくてもデフォルトで初期値に nil が代入されます。
Optional型nil許す型なので、アクセスしたとき値が無くてもエラーやクラッシュではなく nil が返ってきます。
もしこれを Optional型 ではなく String型 などで宣言していれば、値が無い( 初期値が無い )のにアクセスしてしまいエラーやクラッシュする原因になります。

次は非必須項目に値を入れてアクセスしてみます。

//必須項目と非必須項目がある場合の例
let name:String //必須項目
var mail:String //必須項目

//Optional型で非必須項目の宣言
var phone:String?//非必須項目
var birthday:String? //非必須項目

//必須項目に値を代入
name = "hoge"
mail = "aaa@123.com"

//アクセス
print("\(name)のアカウント情報") //hogeのアカウント情報
print("アカウント名 \(name)") //アカウント名 hoge
print("メールアドレス \(mail)") //メールアドレス aaa@123.com

//非必須項目にアクセス
print("電話番号 \(phone)") //電話番号 nil
print("誕生日 \(birthday)") //誕生日 nil

//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//非必須項目に再度アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

非必須項目にも値を代入して再度アクセスしました。
値を入れずにアクセスした時は nil が代入されていましたが、値を入れてアクセスすると nil ではなく、
phone には Optional(“123-4567-8910”)
birthday には Optional(“20XX/XX/XX”)
が代入されています。
String型 の値を代入したのに、この 2 つの変数は Optional String型 で宣言されているので、頭に Optional が付いた状態になっています。
この状態では print関数 で中の値は確認できますが、実際に値を使うことはできません。
使おうとすると、アンラップする必要があります!とエラーが出ます。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//非必須項目に再度アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//Optional String型の変数をString型の変数に入れようとするとエラー
var phoneNumber:String = phone //エラー
optionalコードimg

アンラップ

アンラップとは、ラップ( 包装 )の反対語でラップを外すこと。と表現されている事が多いと思います。
Optional型 はラップ( 包装 )された状態で、このラップを外すことをアンラップと言います。

Optional型の値を 非Optional型 の値と同様に扱いたいときは、アンラップ( unwrap )という処理をしなければ使うことができません。

アンラップする方法

  • ?? 演算子( nil合体演算子 )
  • ! 演算子( 強制アンラップ )
  • if-let文 ( オプショナルバインディング )

この他にも guard-let などいくつかアンラップする方法がありますが、今回は上記の 3 種類を使ってアンラップしていきたいと思います。

?? 演算子 ( nil合体演算子 )

?? 演算子を使ったアンラップの書式

Optional型の値 ?? アンラップ失敗または、nilだった場合に入る値(デフォルト値)

左辺の Optional型の値 がアンラップ可能であれば アンラップした値 を代入し、
アンラップ失敗( 値が nil ) であれば右辺の デフォルト値 を代入する演算子です。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//??演算子(nil合体演算子)
var phoneNumber:String = phone ?? "アンラップ失敗"

//アンラップ後に再度アクセス
print("電話番号 \(phoneNumber)") //電話番号 123-4567-8910
print(type(of: phoneNumber)) //String

Optional型変数 phone がアンラップ可能であれば アンラップした値変数 phoneNumber に代入します。
もしアンラップ失敗や、そもそも値が nil であれば右辺の “アンラップ失敗” という文字列を phoneNumber に代入します。

アンラップ前の phone の値には Optional と頭に付いていましたが、
アンラップ後の値を見るとOptional が取れて普通の String型 にアンラップされています。これで String型 の値として扱うことができるようになりました。
アンラップが成功した場合と失敗した場合の両方の処理を書くので、安全にアンラップする方法だと思います。

! 演算子 ( 強制アンラップ )

! 演算子を使ったアンラップの書式

Optional型の値!

Optional型の値 の後に ! を書くことで強制的にアンラップする事ができる演算子です。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//!演算子(強制アンラップ)
var phoneNumber:String = phone!

//アンラップ後に再度アクセス
print("電話番号 \(phoneNumber)") //電話番号 123-4567-8910
print(type(of: phoneNumber)) //String

Optional型変数 phone の後に ! を書いて強制的にアンラップした値を 変数 phoneNumber に代入します。
アンラップ後のアクセスした結果を見るとアンラップされているのが分かります。

※ ただし ! 演算子( 強制アンラップ )は、 Optional型の値nil だった場合に強制アンラップするとプログラムがクラッシュしてしまいます。
値が nil なのに( アンラップの対象が無いのに )強制アンラップしているためです。
なので nil の可能性がある場合、値が入っているか分からない場合は使えません。
確実に値が入っていると分かっている場合に使います。

if-let文 ( オプショナルバインディング )

if-let文 を使ったアンラップの書式

if let 定数名 = Optional型の値 {
    アンラップに成功したらこの中の処理が実行
} else {
    アンラップ失敗したらこの中の処理が実行される
    else文を書いていない場合、このif-let文はスキップ
}

Optional型の値 がアンラップ可能であれば、アンラップした値を定数に代入し、{ } の中の処理が実行されます。
アンラップ失敗( 値が nil )であれば else文{ } の中の処理が実行されます。

else文 を書かなかった場合、アンラップ失敗( 値が nil )であれば if-let文 はスキップされます。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//if-let文(オプショナルバインディング)
if let phoneNumber = phone {
    print(phoneNumber) //電話番号 123-4567-8910
    print(type(of: phoneNumber)) //String
} else {
    print("アンラップ失敗")
}

Optional型変数 phone がアンラップ可能であれば アンラップした値定数phoneNumber に代入し、{ } の中の処理を実行します。
アンラップ失敗( 値が nil )であれば else文{ } の中の処理が実行されます。
このアンラップ方法も アンラップが成功した場合と失敗した場合の両方の処理を書けるので、nil の値をアンラップしようとしてクラッシュするなどのエラー回避につながります。

エラーやクラッシュを回避するためにも、できるだけ強制アンラップは使わず、?? 演算子や if-let文 でアンラップした方が安全だと思います。

お疲れ様でした。
以上 Optional型 についてでした。

次回は while文 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

投稿者: tomo

30代経験ゼロからプログラミングを始めて、趣味でiOSのアプリを作って遊んでる人。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です