https://youtu.be/aUskHdFn1iU
今回も オプショナル
の機能を実用面から眺めていきます。前回の話と重なるところもありそうですけれど、引き続き The Swift Programming Language の流れに則って 強制アンラップ
や オプショナル バインディング
といった、初歩的かつお世話になる機会の多い機能をおさらいしていくことになりそうです。よろしくお願いしますね。
——————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #150
00:00 開始 00:11 今回の展望 01:12 強制アンラップ 01:33 値を持つことが確実かが重要 05:39 Swift ではあまり使わない nil 判定の書き方 06:34 強制アンラップと実行時エラー 07:35 確実なところで使っていく 08:44 不必要にオプショナル型を使わない 09:37 自動でアンラップされるオプショナル型 11:05 値を表示したいのか、オプショナル型を表示したいのか 13:00 オプショナル型を Any に渡すと警告が出る理由は? 14:23 Any に代入されているオプショナルの値を取り出す術がない 19:11 オプショナルのまま渡すのか、その値を渡すのか 21:36 プロトコルを使って値を適切に扱う 22:52 Any でのオプショナルの扱いには注意 24:50 Optional.unsafelyUnwrapped 28:06 unsafelyUnwrapped の利用場面 29:49 クロージング ———————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #150
はい、では始めていきましょう。今日は引き続きオプショナルについて話します。その実用的な話というか、基本的に多く使われる機能について進んでいきます。前回も触れた内容ですが、今回はその続きですね。
まず、前回紹介したさまざまな書き方や、良い書き方について話した部分を踏まえ、改めてオプショナルの基本事項を整理していきましょう。本に基づいて進める勉強会ですので、前に話した内容が重複することもありますが、その点はご容赦ください。
さて、次に進みましょう。今回のテーマは強制アンラップです。前回も詳しく話しましたが、再確認のためにもう一度取り上げます。オプショナル値を含むかどうかはプログラマーが確信できれば、そのオプショナル値の後ろに !
(ビックリマーク)を付けて値にアクセスできます。
ここで重要なのは「オプショナル値を含むことが確信できれば」という部分です。ただ単に !
を付ければ値にアクセスできる、というだけでなく、確信を持って使うことが求められます。特にプログラミングに慣れている人にとっては、これが自然な発想になるかもしれませんが、この一言が入ることで、発想がなかった人にも適切に指示が伝わるようになっています。
次に進みます。この !
は、オプショナル値が間違いなく存在していることがわかっている場合に、その値を使うことを強調しています。この表現の持つ効果は、プログラマーにとって大変重要です。 !
を使うことで、そのオプショナル値を強制的にアンラップできます。このアクションを「強制アンラップ」と呼びます。
Swiftではこの強制アンラップがよく使われます。他のプログラミング言語でも、同様の機能があります。たとえば、そのインスタンスが nil
でなければ、値を取り出して使うといったものですね。これに関しての詳細は、コントロールフローのセクションに記載されていますが、今回はそこまで深くは触れません。
強制アンラップは非常に便利ですが、一方で失敗するとプログラムが強制終了します。 なので、バリューとしてInt型のオプショナルがあったとして、何か値が入っていればこれを表示する際に強制アンラップすれば、普通に速やかに取得できます。ただし、値がnilの場合は、ここでランタイムエラーが発生してしまいます。これが重要なポイントです。つまり、オプショナルの値を強制アンラップしたときにnilだった場合、アプリケーションが落ちてしまうのです。これが強制アンラップの大きな特徴です。
ランタイムエラーが発生することが嫌われがちです。だからこそ、システムやコンパイルタイムにエラーを検出しようとする機能やエラーハンドリングの手立てが用意されているわけですが、それでも強制アンラップを使うことが避けられない場面もあります。
中には、「必ず値が存在する」という前提がある場合もあります。そういった場合には、強制アンラップを積極的に使っても良いという考え方が最近は浸透しつつあります。以前はランタイムエラーを嫌うあまり、「強制アンラップを使うべきではない」という意見が強かったです。しかし、現在ではその考え方が少し変わってきているようです。
もちろん、そもそもIntが値を持たない可能性があるならば、オプショナルを使うことが推奨されます。オプショナルであれば、強制アンラップのリスクを避けつつ、安全に値を取り扱うことができるからです。しかし、何らかの事情でオプショナルを使用せざるを得ない場合、その値が絶対に存在するという状況であれば、強制アンラップを使うのも一つの方法です。
例えば、Swiftには強制アンラップを簡便にするために、!
マークを使った方法があります。これにより、わざわざ毎回強制アンラップと書く手間を省くことができます。これは便利な方法であり、使い方によっては非常に有効です。
他にも細かな警告が出る場合があります。例えば、ここでswift
のオプショナルを渡した際に、それをそのまま表示するのか、オプショナルの中身を表示するのかが明確でない場合です。普通に考えれば分かりそうですが、これは意図的なのか、単に慣れていないからなのか、色々な理由が考えられます。
例えば、Intのオプショナル型がnilの場合、print
関数に渡した場合にはnil
と表示されます。これはよくある話で、慣れていけば大した問題ではないですが、最初のうちは戸惑うこともあるかと思います。その点を踏まえ、特に初心者向けには適切なコメントや説明が求められます。
このように、オプショナルの取り扱いには注意が必要ですし、強制アンラップはリスクを伴います。しかし、状況に応じた適切な使い方を学ぶことで、Swiftのプログラミングがよりスムーズに進むようになります。 日付を Date
型で表示するプログラムを組んでいると仮定しましょう。例えば、ユーザーに最終更新日を表示する場合があります。このとき、オプショナルな Date
型を使ってしまうことがあります。Swiftではオプショナルのままではその中身が直接表示されません。
他のプログラミング言語では、例えばオプションが nil
の場合、そのままの表示が許可される仕様があるかもしれませんが、Swiftではオプショナルの取り扱いには注意が必要です。初期化が終わっていないままオプショナルの Date
を表示しようとすると、正しく表示されないことがあります。
なので、オプショナルの値を使用する際は、アンラップする必要があります。例えば、このようなコードを書いてみましょう。
if let unwrappedDate = optionalDate {
print("最終更新日: \\(unwrappedDate)")
} else {
print("最終更新日が設定されていません")
}
オプショナルのアンラップをしない場合、意図しない動作を引き起こす可能性があります。オプショナルが意図的に渡されたものであることを確認するためのワーニングも、Swiftには存在します。こういったワーニングは、開発者に対してコードのミスを防ぐための重要な指標となります。
もう一つの例として、任意のオプショナルを受け取って真偽値を返す関数を書くこともできます。例えば、以下のような関数が考えられます:
func isNonNil(optional: Any?) -> Bool {
if let _ = optional {
return true
} else {
return false
}
}
この関数は、オプショナルの値が nil
でない場合 true
を返し、nil
である場合は false
を返します。
let someDate: Date? = nil
print(isNonNil(optional: someDate)) // false
このように、オプショナルを適切に処理することで、予期しないエラーや動作を防ぐことができます。また、Swiftのオプショナルバインディングを活用することにより、コードの可読性を高めることもできます。 値自体はどのように取得するのでしょうか。ディレクトリの中をたどっていく必要があります。少し難しく感じるかもしれませんね。filter first
を使用する場合、それが0
であるならば、要素が存在しないということです。その場合、オプショナルを使用することで次に進むことができるでしょう。
強制的にアンラップするのも避けたいので、オプショナルとして処理を続けるのは良い方法です。しかし、オプショナルのままで値を渡すべきか、それとも具体的な値にして渡すべきかを検討することが重要です。このような判断はコードを書く上での重要なポイントになります。
以前、JSONカードを作成する際に、typealias JSON = Dictionary<String, Any>
といった方法で対応していました。しかし、オプショナルが含まれると結構厄介になりました。そのような状況では、事前に確認するか、他の方法を用いるかという選択が必要です。警告が出るようになったことで、意図しない混入が減少し、ありがたいと感じることも多くなりました。
例えば、プリント文で警告が出るのも、このような厄介な問題を防ぐためです。また、最新バージョンでは、null
が入りにくいようになっている点も嬉しい点です。独自のプロトコルを作成して、必要な型に適合させることも可能です。
JSONがサポートしている型に対して、適切に対応することが求められます。この場合、空文字などを返す方法も考えられます。独自のプロトコルを使って、StringやInt、Boolean、Array、Dictionaryなどに対応させることが可能です。これにより、以前は難しかったことが、Codable
を使うことで大いに改善されました。
話がどこに向かっていたのかを少し忘れてしまいましたが、エニーとオプショナルの周りでの注意点については触れられたと思います。身にしみることが多いので、実際にコーディングしてみると理解が深まるでしょう。
警告が出るおかげで、これらの問題に引っかかることが少なくなりました。例えば、強制アンラップ
のような操作に警告が出るのは、曖昧な操作を避けるためです。警告が出る理由についてしっかり理解することが究極的に重要です。 警告を消すために強制アンラップを使用するかどうかについて話します。この方法は値を使用するか、デフォルト値を与えるかという二つの選択肢がありますね。警告は一般的に細かく出ることが多いですが、今回は一つの警告について話します。
強制アンラップといえば、普通のオプショナルにしたほうが良い場合があります。例えば、値にデフォルト値を付与する場合や、安全性が求められる場合です。Date
型の変数に対して強制アンラップを行うときは、date.unsafelyUnwrap
というプロパティがありますが、これは使用する場面が限られます。
強制アンラップは、その名の通り値が存在しない場合にクラッシュを引き起こす可能性があります。ランタイムエラーが発生する例として、オプショナル型の変数に強制アンラップを行って値が存在しないケースが挙げられます。このプロパティを使用することで一見して同様の操作が行えるように見えますが、実際には違うエラーが発生します。
他のプロパティと比較すると、unsafelyUnwrap
はリリースビルド時には値が存在しなかった場合、何もなかったかのように処理されることがあります。特にコンパイラーの最適化レベルによっては、意図しない動作が発生する可能性があります。
ビックリマークを使った強制アンラップ(!
)の場合、値がないと必ずフェイタルエラーが発生します。それに対してunsafelyUnwrap
は、コンディション次第で処理の結果が変わります。プリコンディショナルのチェックが必要な場所ではビックリマークを使ったほうがよく、そうでなければunsafelyUnwrap
が使われることもあります。
たとえば、URLのオプショナルを強制アンラップする場合、成功することが確実な場合はビックリマークを使用し、条件によって変わる場合はunsafelyUnwrap
を使用することでパフォーマンスが向上することがあります。これは、例えばURLの文字列が https
で始まる場合などです。
最適化レベルに応じてチェックが異なり、リリースビルドではそのチェックが取り除かれている場合もあります。よって、適切に使い分けることが重要です。
以上、強制アンラップについての話でしたが、あまり知られていない部分も多く興味深いですね。これらの知識を使いながら今後のコードを書いていくと、より安定した実装ができるかもしれません。今日はここまでにしましょう。お疲れ様でした、ありがとうございました。