Caution
お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。
Swift
入門編
- トップページ
- Swift入門編 - nilとオプショナルについて
nilとオプショナルについて
みなさまどうも。いかがお過ごしでしょうか。
この記事を執筆しているのは11月初旬なんですが、急に寒くなってきています。デスクワークをx0年も続けると急激な気温差に体がついてこず困ったものでございます。
さて、続きまして『nil』(ニル)とか『Optional型』(オプショナルがた)についてやっていきましょう。
さっそくですが『nil』に関しての注意点がございます。他のプログラム言語にも『nil』、または『null』という値が用意されていますので他言語に精通している方は「他の言語の『nil』とか『null』とかと同じような感じだろぉ」と考えてしまいがちですが、Swiftの『nil』は他の言語の『nil』や『null』とは大分構造が違いますので気をつけて下さい。ここを間違えるとドツボにはまるので要注意です。著者もやられたのは内緒です。
というわけで、ちょいとこれまで紹介した内容を振り返ってみましょう。以下のサンプルを見てみて下さい。
var n: Int
上記のサンプルはInt型の変数『n』を定義しただけの処理になります。中身は空です。そんでもってこの変数『n』を『print()』とかで出力しようとするとこうなります。
var n: Int print(n) // エラーです。
エラーがでちゃっていますね。このエラーは「存在しないものを出力しようとしているから」って感じのエラーになります。
このように「存在しないものをこにょこにょしようとするとエラーになっちゃう」というのがコンピューターの世界です。コンピューターは物理的に厳密な演算を行っていくような構造になっているため、上記のような「存在しないものを扱う」といったパターンはちょっと苦手なんですよね。これは他のプログラム言語でも同じで、はるか昔からこんな感じになってます。
しかし、プログラミングをするにあたって「存在しない可能性がある」という処理を組めないのは少々問題ですよね。なのでほぼすべてのプログラム言語では「存在しないもの」を扱うための処理が何パターンか用意されています。
Swiftで「存在しない可能性があるもの」を扱う場合は『Optional型』というものを使用するのが一般的です。
というわけでちょいと『Optional型』を試してみましょう。『Optional型』で定義するには『Optional<データ型>』という感じで記述します。こんな感じですね。
var n: Optional<Int>
今変数『n』の中身は先ほどと同じく空の状態です。ではこの変数『n』を『print()』で出力してみましょう。するとこうなります。
var n: Optional<Int> print(n) // 『nil』が出力されます。
いかがでしょうか。先程はエラーになっちゃってましたが、今回はエラーにならずにバッチリ『nil』が出力されていますね。
このように「『Optional型』でデータをラップしておくと値が存在しない場合は『nil』を返すようになる」という挙動になるためエラーを回避することができます。これが『Optional型』の基本的な使い方になりますね。
ちょっと注意点ですが、上記のサンプルの場合は「『Optional型』の機能を持ったInt型で定義している」のではなく「Int型のデータを『Optional型』でラップしている」というのがイメージとしては正しいです。ちょっとした言葉のあやですが、間違えやすいので要注意です。
プログラミング言語では『値を返す』という表現をされることが多いです。『値を返す』とは『(演算式に)演算結果を返して(教えて)もらう』といった意味になります。
少し数学を思い出してみましょう。以下のような計算式があったとします。
1 + 2
これは『1 + 2』なので答えが『3』になるわけですが、プログラミングでは「『1 + 2』の演算式(計算式)は『3』を返す」という表現をします。
数学における『演算式(計算式)』が『関数』で『答え』が『値』に該当するような感じですね。
ちょっとした言葉のニュアンスとなりますが、プログラミングに慣れていないと混乱しがちなのでよく分からなくなったら単純な計算式に当てはめてみるとイメージが掴みやすいかもしれません。
そしてSwiftには『Optional型』と似ているのですがちょっと違う、そんなセンチメンタルな型があるので紹介します。
その名も『Implicitly Unwrapped Optional型』です。『Implicitly Unwrapped Optional型』も、『Optional型』と同じく中身が存在しない場合に『nil』が返ってくるようになる処理になります。
なんだか名前が長ったらしいですね。日本語に訳すと「自動で『アンラップ』される『optional型』」って感じになります。
なんのこっちゃってお話なんですが、『アンラップ』については後述しますので定義方法を確認しておきましょう。こんな感じです。
var n: ImplicitlyUnwrappedOptional<Int>
上記のように『ImplicitlyUnwrappedOptional<データ型>』とすることで『Implicitly Unwrapped Optional型』でラップしたデータ型として定義できます。
そして『Optional型』と『Implicitly Unwrapped Optional型』を定義する際に、ちょっとした『シンタックスシュガー』が用意されていますのでそれも紹介しておきます。以下のサンプルを見てみて下さい。
以下のサンプルは変数『n』を『Optional型』、変数『_n』を『Implicitly Unwrapped Optional型』として定義しています。
var n: Int? // 『Optional型』です。 var _n: Int! // 『Implicitly Unwrapped Optional型』です。
なにやら『Int』の後に『?』とか『!』がくっついていますね。
このように『データ型?』とすると『Optional型』、『データ型!』とすると『Implicitly Unwrapped Optional型』でラップされたデータ型として定義することができます。
『Optional<データ型>』とか『ImplicitlyUnwrappedOptional<データ型>』とかは長ったらしくて書くのがダルいので『?』または『!』を使用するのが一般的です。
『シンタックスシュガー』とはプログラミングにおいて、記述がしやすいように既に存在する構文に別の記法を与えたものの事を指します。
今回の場合ですと『Optional<データ型>』を『データ型?』って簡潔に記述できるようになってますよね。こういうのが『シンタックスシュガー』に該当します。
良く参考書とかででてくるので覚えておくと良いかもです。
では続いて『Optional型』を使用する際の注意点とかを確認していきましょう。以下のサンプルを見てみて下さい。
var n: Int? = 1 var _n: Int = 2
上記のサンプルは『Optional型』でラップしたInt型の変数『n』と普通のInt型の変数『_n』を定義しています。
Int型ならば加算処理ができるはずですよね。しかし、試してみるとあら不思議。
var n: Int? = 1 var _n: Int = 2 print(n + _n) // エラーです。
『n + _n』ってところに注目です。加算して数値『3』が出力されるかと思いきやエラーになっちゃっています。
これはなぜかというと、一方が『Optional型』なのでそのままの状態だと演算できないのが理由だったりします。
他の言語では「暗黙の型変換」と呼ばれる、プログラマーの見えないところでこっそり型の変換を行ってくれる処理が存在していたりするのでそのまま演算できちゃったりすることも多いですが、Swiftの場合は「暗黙の型変換は行わないぜ!」という思想で出来ているため上記の処理はNGです。
なので状況によっては『Optional型』で定義したデータを演算する場合に、元に戻す処理をかます必要があったりします。この元に戻す処理が、先程出てきた『アンラップ』と呼ぶ処理になります。
というわけで先程の処理を『アンラップ』して演算できるようにしてみましょう。『Optional型』を『アンラップ』するにはこうします。
var n: Int? = 1 var _n: Int = 2 print(n! + _n) // 数値『3』が出力されます。
上記のサンプルの『n! + _n』ってところに注目してみましょう。『n!』という風に変数『n』に『!』がくっついていますね。
このように変数とかに『!』を付けることでそのデータを『アンラップ』し、元のデータ型に戻すことができます。
ちなみに先程も『ImplicitlyUnwrappedOptional<データ型>』のシンタックスシュガーとして使用する『!』が出てきていましたね。
ちょっとややこしいのですが、変数などを定義するときに『!』をくっつけると『ImplicitlyUnwrappedOptional<データ型>』のシンタックスシュガー、すでに定義されている変数などに『!』をくっつけると『アンラップ』になります。
頭こんがらがってしまいがちなので間違えないようにして下さい。同じ記号を使ってるので非常に間違いやすいです。著者もよく「どっちだったっけ?」ってなってます。
var n: Int! = 1 // この『!』は『ImplicitlyUnwrappedOptional<データ型>』のシンタックスシュガーです。 print(n!) // この『!』は『アンラップ』です。
というわけで『Optional型』で定義された変数とかを使用する際は『アンラップ』する必要がある、と覚えておきましょう。
しかし、いちいち『アンラップ』するのも少々面倒くさい、もっと俺のことを気にしてくれないか、もっと俺のために頑張ってくれないか、もっと俺のことを愛してくれないか、そんなロマンチックな気分になってしまう時は誰だってあります。
そんな時は先程紹介した『ImplicitlyUnwrappedOptional型』を使ってみましょう。以下のサンプルを見てみて下さい。先程の処理の変数定義を『?』ではなく『!』を使ってみました。するとあら不思議。
var n: Int! = 1 var _n: Int = 2 print(n + _n) // 数値『3』が出力されます。
上記の『n + _n』のところに注目です。先程のように『!』を使って『アンラップ』していないにも関わらずそのまま演算できてしまっています。
このように『ImplicitlyUnwrappedOptional型』は使用するときに自動で『アンラップ』してくれる『Optional型』といった感じになります。
必ず『アンラップ』してから使う予定の変数とかは『ImplicitlyUnwrappedOptional型』として定義してしまうと色々と楽ちんです。状況に合わせて使い分けていきましょう。
さて、『アンラップ』する時に使う『!』を紹介しましたが、実は『?』を使って『アンラップ』することもできます。ちょいと以下のサンプルを見てみましょう。
var s: String?
上記のサンプルはString型を『Optional型』でラップした変数『s』を定義した感じですね。『Optional型』でラップしているため、変数『s』は『nil』である可能性が出てきます。
そしてちょっと話が逸れますがSwiftのString型で定義した文字列(データ)は『.characters.count』という処理が用意されており、これで文字列を数えることができるようになっています。
記述方法は『文字列.characters.count』という感じです。その文字列の文字数がInt型の数値で返ってくるような処理になります。以下のような感じですね。
print("".characters.count) // 『0』が出力されます。 print("abc".characters.count) // 『3』が出力されます。
では先程の定義したString型を『Optional型』でラップした変数『s』で『.characters.count』を使うと仮定しましょう。
変数『s』に何かしらの文字列が入っていれば『.characters.count』を使用することができますが、もし『nil』になってしまったらどうでしょう。
そうすると『nil.characters.count』という形になってしまいますよね。『nil』には『.characters.count』が存在しませんのでバシッとエラーになってしまいます。
var s: String? s = "abc" // 文字列を代入します。 print(s!.characters.count) // OKです。『3』が出力されます。 var _s: String? print(_s.characters.count) // 変数『s』は『nil』となるためエラーです。 print(_s!.characters.count) // 『アンラップ』してもダメです。
このような時に『!』ではなく『?』を使ってみましょう。『!』ではなく『?』で『アンラップ』した場合、「『アンラップ』した際に『nil』だったら続く処理をエスケープし『nil』を返す」という処理になるためエラーを回避することができます。このような感じですね。
var s: String? print(s?.characters.count) // 変数『s』は『nil』となるため続く処理は行われず、『nil』が出力されます。 s = "abc" // 変数『s』に文字列を代入します。 print(s?.characters.count) // 変数『s』は『nil』ではないため『.characters.count』が実行され『Optional(3)』が出力されます。
こんな感じで「『nil』となった場合のエスケープ処理」が簡単に組めるのが『?』になります。かなり便利なので覚えておいて下さい。
そして『?』を使用する際にちょっと注意点があります。『?』は『Optional型』のデータに続けてプロパティを取得したり、メソッドを呼び出す場合に使用できる構文になります。
ちょっとプロパティとかメソッドとかの解説がまだなので謎な感じがしてしまうかもしれませんが、以下のように『.characters.count』とかを続けて記述する場合は『?』を使ってしまってOKです。
var s: String? = "" print(s?.characters.count) // OKです。
でも以下のように『s?』とだけ書くとNGです。
var s: String? = "" print(s?) // NGです。
ここ間違えないようにして下さい。著者も覚えたての頃間違えていて本当の意味で「?」となってました。
そしてもういっちょ注意点があり、先程の処理の最後の『print(s?.characters.count)』の実行結果に注目してみて下さい。数値『3』が来るかと思いきや『Optional(3)』が出力されていますね。
このように『?』を使用した場合は『アンラップ』されたデータではなく『アンラップ』される前のデータが返ってきます。ですので以下のような処理を組むとエラーになります。
var s: String? = "abc" print(s?.characters.count + 10) // エラーです。
これはなぜかというと『s?.characters.count』で取得したデータは『Optional型』なので、そのままの状態だとInt型の数値『10』との演算はできません。
なので『!』を使って『アンラップ』する必要があったりします。こんな感じですね。
var s: String? = "abc" let n = s?.characters.count // 一旦定数とかで受け取ります。 print(n! + 10) // 数値『13』が出力されます。『!』で『アンラップ』するのがポイントです。
ここも間違いやすいところなので注意して下さい。
あと先程のサンプルの変数『s』が『nil』になった場合にも注意が必要です。
変数『s』は『Optional型』なので『nil』となってしまう可能性がありますよね。ということは『n! + 10』が『nil! + 10』となってしまう場合もあるということです。そうするとさくっとエラーになっちゃいます。
var s: String? // 変数『s』は何も代入されてないため『nil』になります。 let n = s?.characters.count // 定数『n』には『nil』が入ります。 print(n! + 10) // 『nil! + 10』となってしまうためエラーです。
なので「『nil』が来る可能性があるときはちゃんと分岐処理を書いておく」という心構えが必要だったりします。
そこでご紹介したいのが『??』という演算子です。これを使うと『nil』かどうかの分岐処理を行う事ができます。使い方はこんな感じです。
var s: Int? print(s ?? "nilです") // 『nilです』が出力されます。
上記の『(s ?? "『nil』です。")』ってところに注目です。
『??』は「左辺が『nil』なら右辺を、左辺が『nil』じゃなかったら左辺を返す」という処理を行う演算子です。
上記の場合の変数『s』は『Optional型』でまだ何も代入していない、つまり『nil』となるので右辺の文字列『nilです』が出力されていますね。
では変数『s』に何か値を入れてあげましょう。するとこうなります。
var s: Int? print(s ?? "nilです") // 『nilです』が出力されます。 s = 1 print(s ?? "『nil』です") // 『1』が出力されます。
今度は『1』が出力されていますね。
この『??』の使い方はこんな感じになります。結構便利なんで機会があったら使ってみて下さい。
今回紹介した『nil』だったときにエスケープしてくれる『?』は『Optional Chaining』(オプショナル・チェイニング)って名前が付いていたりします。
var s: String? = "abc" let n = s?.characters.count // この『?』が『Optional Chaining』になります。 print(n! + 10) // 数値『13』が出力されます。
たまに参考書に出てくるのですが、まあエスケープするための『?』ってだけなので名前は特に覚えなくても良い気がします。
それと、先の記事で詳しく紹介する『if文』ってやつがいるのですが、こいつを使って『nil』だった場合に処理を分岐させることができます。こんな感じですね。
var s: String? if s != nil { print("『nil』じゃないです。") } else { print("『nil』です。") }
他の言語に精通してる方だとこっちの方が慣れ親しんだ感じと思います。
そんでもって、上記のように『if文』と定数を使って『nil』かどうかの分岐を行う処理は『Optional Binding』(オプショナルバインディング)って呼ばれたりします。こちらも覚えなくてもOKな気がしますが、一応紹介しておきます。
というわけで以上になります。かなりややこしい上にだいぶ長くなってしまったんですが大丈夫そうでしょうか。
続いての記事では『関数』とかについてやっていきます。ではではこの辺で。またお会いしましょう。
この記事は桜舞が執筆致しました。
著者が愛する小型哺乳類 |
桜舞 春人 Sakurama HarutoISDN時代から様々なコンテンツを制作しているちょっと髪の毛が心配な東京在住のプログラマー。生粋のロングスリーパーで、10時間以上睡眠を取らないと基本的に体調が悪い。好きなだけ寝れる生活を送るのが夢。ゲームとスポーツと音楽が大好き。誰か髪の毛を分けて下さい。 |
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。