https://youtu.be/rTweINkxhVA
今回は オプショナル
の if
文の話の続きから、実際に オプショナル
を使っていく観点からの基礎に着目して眺めていきます。基本的なところで慣れるとあまり意識を向けなくなるところでもあると思うので、この機に改めて復習しながら関連する事柄についても寄り道しつつ進めていこうと思っています。よろしくお願いしますね。
——————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #149
00:00 開始 00:22 今回の展望 00:42 nil 判定するときの様々な書き方 01:45 値が nil のときに何かをする場面を考える 02:40 オプショナルバインディングでの nil 判定 02:48 オプショナルバインディングでは let が必須 03:37 ワイルドカードパターンで判定する場面 04:21 列挙型として判定する場面 04:37 列挙子パターンで判定する場面 05:16 guard を使って表現する案 06:55 識別子パターンで判定する表現 10:17 配列と compactMap を活用する方法 10:53 オプショナルの map を使って表現してみる 11:48 if let 省略表記で表現できる? 13:12 データサイズの違いで判定できたるする? 18:42 ハッシュを使って判断はできる? 19:42 guard を用いた表現方法 21:53 どの書き方が推奨されていそう? 23:24 強制アンラップの是非について 25:47 リフレクションを使ったアイデア 26:31 Any にオプショナルが入っているかを確認する手段 28:00 オプショナルチェイニングの先でレシーバーを使う場面 28:56 メタタイプを用いてオプショナル型かを判定する 30:25 map のときに出てくるかもしれない可能性 32:19 クロージング ———————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #149
では、始めていきますね。今日はオプショナルについて話します。前回もオプショナルについて実用的な部分に触れましたが、今日は改めて基礎的なところから説明します。
まず、オプショナルとnil
との比較についてですが、この話はもう散々してきたので、今日は基本的な書き方に集中します。オプショナルがnil
かどうかを判断する方法についてですね。Swiftが出てからしばらくの間、この書き方についてどれが最適かという議論がありました。
一般的な方法としてはオプショナルバインディングです。例えば、バリューの中身がnil
だった場合に「外で要請終了する」などのメッセージを出すためのコードを書きたいとき、値があるときはオプショナルバインディングを使うと簡単に書けます。これはすぐに理解できると思います。
if let value = optionalValue {
// 値を使う処理
} else {
// nilの場合の処理
}
ここで注目すべきなのは、値を捨てる場合の書き方です。例えば、以下のようにワイルドカードを使うこともできます。
if let _ = optionalValue {
// 処理
}
この方法は、レッド自体も特に必要ありません。これによって、読み取り専用などを気にする必要もなくなります。
次に考えられる方法としては、オプショナルパターンを使ったものです。スイッチ文でも同じようなことができます。
switch optionalValue {
case .some(let value):
// 処理
case .none:
// 処理
}
また、if case
文を使うことも可能です。
if case let value? = optionalValue {
// 値を使う処理
}
オプショナルがnil
かどうかを確認する方法は他にもありますが、ここでは代表的な方法だけを紹介しました。特に、スイッチ文を使う場合はもう少し複雑なパターンマッチングを行うこともできます。このように、オプショナルを扱う際には様々な方法があり、それぞれの状況に合わせて使い分けることが重要です。
例えば、fatalError
を使ってエラーハンドリングをする場合もありますが、この場合のコード例も紹介します。
if optionalValue != nil {
fatalError("Unexpectedly found nil while unwrapping an Optional value")
}
このように、Swiftではオプショナルとnil
の取り扱いに関する多様なアプローチが存在します。用途に応じて最適な方法を選ぶことが重要です。 「えっ、なんかエラーがまた出てますね。あ、ここでエラーが出ているんですね。これは結局エラーが出てないのかな?あ、網羅していない部分があるって表示が出てますね。ここですね、これが網羅していないnil
のケースです。便宜上、こういう風に書いておきますが、これでエラーがなくなるのかな。そうそう、値を使ってないのでワイルドカードパターンにする手もありますし、もしワイルドカードパターンを使う場合は、enum
ケースパターンだけで済みます。
最初に振り分けて、一度抜けてから特定のパターンに対して適切に処理していく方法もあります。Swiftの場合はwhere
も使えるから、わざわざ早期に答えを決める必要がない場合もあり得ますね。ただ、ケースによってシンプルなコードになるかもしれません。この発想はいいですね。他にnil
かどうかを判定する方法として、if
文や配列のcompactMap
を使うこともできます。compactMap
を使うのは確かにミソですね。nil
だったら捨ててしまうという方法です。
この発想はなかなか面白いですし、いいと思いますね。オプショナル型を使ったメソッドはmap
がよく知られていますが、他にも酷いコードを思いつきました。たとえば、if let
とmap
を使うと、nil
だったらmap
が通らないので、nil
を返す部分があります。これは不便ですね。
他にもオプショナルバインディングの短縮形であるif let
を使う方法もあります。これは値があるときにだけ使えますが、その値が不必要なときに最適化されるので無駄が減ります。if let
にelse
を併用して、値があった場合の処理を適切に行うことができますね。」 確かにショートハンドを使うという意味では、この書き方が新しいバージョンです。次のXコードだからね。他に何か面白いものがあるか探していますが、無駄なものを排除している中で「これはいいじゃん」というのが見つかるのを期待してやっています。
データサイズについてですが、値があると最適化を考えなければ、メモリがそのVALUEのインスタンス分取られてしまうので、そのメモリアロケートは若干処理が重くなるといったところです。Nil判定でnilがあるかどうかでポインタの先の倍率が変わるなら、それで判定できるのかな、という話もありました。構造体の場合はどうなんでしょうか、分かりませんね。ポインタのnilは確か0を指すはずですが、構造体だと倍率が変わりますよね。オプショナルのデータの待ち方がちょっと違うので、どんな値になっているのか気になりますね。
安全ではない方法ですが、試してみると面白いかもしれません。例えば、Unsafe
系のアクセス方法などです。ただ、そういうことを教えてくれる人もいるでしょう。例えば、UInt
に変えてみるとどうか、あるいはVALUE
がInt
型ならどうかという風に試みます。
エラーが出るかもしれませんが、それも学びの一つです。「Optional
にしないといけない」などといった制約がある場合もあります。Int
型の変数G
を使ってみると、キャストもできますし、オプショナルな変数にした場合の値の変わり方も確認できます。例えば、100
と0
にすると、Optional
のアイコンが確認できるわけです。
このようにして、例えばUInt
やUnsafeBufferPointer
が取れるかどうか試してみるのも良い方法です。そして、Optional
の値がnilかどうか判定してアプリケーション内で利用することができます。この方法を使ってコードの選択肢を広げることも可能です。
これらの変更をIF群で使えばガードも回避できますが、その分コードが増えてしまいます。エラーが出た場合に、エラーの箇所を見つけ出すのも一つの挑戦ですね。VALUE
をバッファーとして渡すときにはどうすればいいか、という点も見落とさないようにします。
今回の使い方に限定されますが、このような書き方は確かに一例として有効です。普通の演算子として使えることを思い出せば、確かにこう書きますね。例えば、特定の条件を満たすときに必要なエクスプレッションパターンを適用する場合でも、有効な手段となります。
これくらいにしておきますが、例えば別のアイデアが出てくるまで考え続けるのも良いかもしれませんね。ただ、一般的にはここまで、3行くらいまででまとめるのが良いようです。 よくあるのですが、今回の話をしようと思ったのは、前回コードを書いた際に警告が出たからです。今回の勉強会では警告が出なかったので、その点を確認してみましょう。バリューを出してみたり、例としてコードにバードを入れてみると、警告が出るかもしれません。これが前回の警告と同じものであれば、見覚えがある人もいるかもしれません。
それを見て、4行目を基本にしているのかなと感じたので、それを紹介しようと思ったのですが、今回は警告が出ていないので自分の勘違いかもしれません。ただ、5行目を使うのと4行目を使うの、どちらが好ましいかという点について議論がなされました。この勉強会で話題になった時には、4行目が良さそうだという意見もありましたが、その根拠は直感的なものでしかありません。また、時間が経つにつれてどれがふさわしいか意見が分かれてくると思います。
例えば、オプショナルの強制アンラップについても、最初の頃は「ビックリマーク(!
)」を使うべきではないという非常に強い意見が一般的でした。しかし、強制アンラップのビックリマークを適切に使えば、コードが明瞭になるという意見もありました。当時は肯定派がそれなりに主張していたわけですが、今ではビックリマークを無闇に使うのは良くないという意見が一般的です。ただ、それなりに使われる場面も増えてきました。
これは非常に良くできた大人の事情で繰り返された仕様の一つです。例えば、インターフェースビルダーを使う時にも見られます。awakeFromNib
のように、保証できない場面では特に便利です。これをいつもオプショナルの基本構文で対応するのは大変です。そういう場合にビックリマークを使うことで、コードが簡潔になります。
次に関数についても話しました。関数一つでコードの意図が明確になることがあります。例えば、isOptional
という関数を作れば、簡潔に意図を説明できます。関数名で説明できるとは、非常に便利です。スタイルについても、オプショナルが特別扱いされることがあると知りました。Any
からオプショナルを特定するのは滅多にないことですが、必要な場合には便利です。オプショナルバインディングでは判定が難しい場合でも、ミラーを使えば取り出せるというのはすごいことです。
このように、コードのスタイルや警告の扱い、オプショナルの使い方など、時代とともに変わることも多いです。勉強会やコメントを通じて新しい知識を共有するのは非常に有意義だと思います。 これね。でも、これオプショナル型の判定しかできないところはどうだろう。えーっと、3つのオプショナル・バリュー、今nil
が入っているでしょ。これで、あ、オプショナル型だね。うん。それで、えーと、v
に与えられていると、え?消しちゃった?あ、nil
になってるか。ここ、便宜上ね、nil
とか入れておいて。エラーしてる?え、ないよね。これで来たね。v
にすると、これもブルーになるよね。うん。だから、インスタンスの中身までは分からない。うん、オプショナル型であるかこれだけだね。その後は、えっと、頑張って見ていく感じかな。
で、あとこれは、えーと、あー、そうか、Foundationとかでね、えーと、アクロデュアティーだ、あとアクロデュアティーね。えっと、F = B
…。あー、なるほど、今回だとね、if let
ないんでね。if let
のメソッド、if
…。あー、なるほどね。これも確かに面白い。そうだね、あったら実行するよ、その中で自分を使うことがあるよみたいなときには、確かにね、ここで最初にね、if let
とかね、頑張らなくていい。これは確かに面白い使い方ですね。有効な使い方。確実にここに来るときにはnil
じゃないよっていうね。
うん、あとイコール、let instance = someOptional
。これは、メタタイプを取って…。あー、なるほど、ExpressibleByNilLiteral
タイプ?あー、NilLiteral
タイプ、これ取るんだ。うーん、ここを込めた後、今おっしゃったんだけどね、えーそうなんだ、ExpressibleByNilLiteral
がアソシエイティブタイプを持ってないからできる傾向なのかな。なるほどね、Mirror
を使うよりは正確な表現かもしれないですね。これね、要はバリューに対してできるっていうわけでしょ。このキーをわざわざ作らなくても、if let
とか、ここでタイプをオープンしないといけないのか。もうちょっといいやり方があるかもしれないですね。でもまぁこういうことですね。うん、なるほどね。
まぁこの最後に紹介した55行目とあとその前に紹介したMirror
を使う方法は、あくまでもね、インスタンスがnil
かどうかではなくて、型自体がオプショナルであるかどうか、中身は問いませんっていうところなんでね。まぁ今回一緒に紹介しましたけど、そこを今度押すと特異なマップになるので注意ですかね。
はい、ではね、これくらいにしますが、何かありますかね。何か見せようかな。おっ、コメントをいただいた、if let map
メソッド。おー、なるほど、if let map
メソッド。そうね、こういう書き方も確かに妥当そうに見えますね。クロージャーなら。うん、そうね、確かに。本当だ。そしてそれに対する意見も面白いですね。これが普通のね、こういうグローバルな…。あとちょっと話し過ぎたね、ここと52行目とね、この53行目。ここの次の話ね、こういう書き方をしなくても、こういう書き方でいけるよねっていう書き方。確かにこっちが有効に働く機会って結構多いと思うんですけど、コメントでいただいたのはね、もちろんこれもいいけれど、この$0
ってね、クロージャーの中で結構使われたりする。map
とかだとそうですよね。相手とかなんかあったときのmap
とかやると、省略するとここでね、$0
が要素になったりする。これを生かしつつなんか似たようなことをしたい。だからこのF
がmap
に渡ったときみたいなこととかも想定してもいいんだ。
ちょっとややこしい例になるけど、具体的に書いちゃったほうがわかりやすいけどね。確かにね、こうすると、$0
と$0
じゃないのと混ざってくるから、こっちのほうがわかりやすかったですね。うん、なるほどね。確かにこのセルフ発展のself! map
っていうのを使うっていう機会も確かに出てくるね。うん、面白いですね。
はい、では今日はこれくらいで終わりにしましょうね。お疲れ様でした。ありがとうございました。