https://youtu.be/SEEQ6rTpQXE
前回は予定していた オプショナルバインディング
の話の前に、その前の勉強会で見ていったことの中で補足しておきたい事が見つかりそれを話す会になりました。今回はこの間に予定していた オプショナルバインディング
の基礎的なところの続きから入っていく見込みです。これまでにだいぶ話した分野ではありますけれど、The Swift Programming Language の方でも丁寧に解説がされているので、それに則って基礎的なところの総浚いをして行きますね。
それと今回はゆめみ社外の人への一般公募はなかったようなので、基本的に社内メンバーのみでの開催になりそうです。よろしくお願いしますね。
————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #158
00:00 開始 00:10 今回の展望 01:33 オプショナルバインディングの使用例の解説 02:14 失敗可能イニシャライザーの可読性 06:08 API 設計でカバーできる範疇かもしれない? 08:50 オプショナルバインディングで型注釈 13:46 オプショナルバインディングとシャドーイング 16:14 オプショナルであることがわかる最適な名前は? 19:47 if let 省略表記 20:45 引数のシャドーイング 22:07 シャドーイングの扱い方 23:40 局所スコープでのシャドーイング 24:27 シャドーイングで隠された値にはアクセスできない 26:33 名前空間を使って隠された側にアクセス 28:47 フリーな関数の名前空間 31:32 guard 節とシャドーイング 35:39 シャドーイングについてのまとめ —————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #158
はい、じゃあ始めていきましょう。今日はオプショナルバインディングの使用例の解説がテーマです。前回の勉強会ではオプショナルバインディングの使用例を扱いましたが、その前に何かエクステンションに関する話があったようです。興味がある人は前回の動画を見直してみてください。
今回の勉強会では、オプショナルバインディングについてのさらなる解説を行っていきます。既に十分に理解している人もいるかもしれませんが、復習として改めて取り上げます。こういった基礎的な内容は何度聞いても新たな発見があるかもしれないので、じっくり学んでいきましょう。
前回の解説では、if let
を使ったオプショナルバインディングの例を取り上げました。例えば、次のようなコードです。
if let actualNumber = Int(someString) {
// `actualNumber` は具体的な `Int` 型として使える
}
この場合、Int(someString)
が変換に成功すると、actualNumber
という非オプショナルのInt
型の定数が生成されます。これにより、安全に actualNumber
を使用することができます。ただし、このサンプルコードでは失敗可能イニシャライザーが使われているため、オプショナルが返されることに注意が必要です。
オプショナルが返されることを明示的に示したい場合があります。そのためには、例えば次のようなアノテーションを使うと良いでしょう。
let actualNumber: Int? = Int(someString)
これにより、actualNumber
がオプショナル型であることが明らかになります。また、他のイニシャライザーが失敗可能かどうかを知る方法として、自動補完や型情報の確認が役立ちます。
例えば、次のようにアノテーションを付けることで、失敗可能イニシャライザーであることを明確にできます。
if let actualNumber: Int = Int(someString) {
// `actualNumber` は具体的な `Int` 型として使える
}
このようにしておくことで、コードを読む他の開発者にも優しい書き方になります。
個人的には、失敗可能イニシャライザーが返してくるオプショナルの型を明示的に示す方法が欲しいと思うことがあります。そうすることで、コードが読みやすく、安全性の向上につながると考えています。
さらに、関数やメソッドの宣言においても、パラメータがオプショナルかどうかを明示することで、エラーを未然に防ぐ設計が可能になります。
このような工夫をしつつ、オプショナルバインディングの使い方について理解を深めていきましょう。発表されたサンプルコードを見直しながら、自分自身でも試してみると良いでしょう。
さて、他にも解決策やアイディアがあれば教えてください。次回は、さらに具体的な使用例や他のプログラミング言語との比較も含めて解説していく予定です。それでは、今日はここまでにしましょう。お疲れ様でした。 全体的に考えてみると、この職種的な部分については、これでいいという感じかもしれませんね。もちろん、あったらあったで良い気もしますが、そう考えると確かに全体的にいろんなところが安全で、ここに戻ってくる感じですね。
オプショナルは例えば、これを半数に100で渡す場面でも同じ選びになりますね。こういうふうに安全に考えれば絶対に間違えることはありません。確かにこれで良い気もしますが、こういう例の時には分からなくなってしまうこともあります。そのような時にはアノテーションを使えば良いわけです。「if let」でアノテーションすればできる気がしますね。忘れてしまいましたが、昔にやったことがある気がします。ちょっと書いてみましょう。
今回については特に意味のないことを書くので、「actualNumber」がInt
型で、Int
をString
に変換します。本当に許されているかな?許されているよね、許されていないなんてことは…。これは自分のPCだけど、せっかくだから使ってみましょう。
例えば「if let」オプショナルバインディングとして、型も指定できます。だから「これはStringだよ」と言うことができます。当然、型が違えばエラーになりますし、オプショナルバインディングを使用することでも、安全にオプショナルとして扱える場合があります。タブタイプであるオプショナルInt
を使えば、コンパイル自体はパスします。結果がグレーなままなので、何か問題が発生しているかもしれません。
そうですね、オプショナルバインディングした際にInt
型かどうかを確認し、オプショナル型を使っているので問題ありません。仮に値が0であれば、しっかりきれいに表示されるはずです。今、動かないのは何だろう…。エクスポートを押してみようかな。そうすれば動くと思います。
はい、動きますね。「actualNumber」はこのようにInt
オプショナル型として扱えます。パターンアノテーションとして使えるので、サブタイプや互換性がある型としても扱えます。例えば、これを「i9NData」にすると、エクスポートが使えましたね。どのエラーだろう…。曖昧なのかな。エニルとか、そういった型にキャストすれば使えるのかもしれません。オプショナルバインディングは便利なので、これを使うといろいろなシーンで役立ちます。
オプショナルバインディングのアノテーションを使った人はあまりいないと思いますが、こういうこともできますよということを覚えておくといいですね。それでは、ちょっと戻りましょう。流れを見ていくと、オプショナルバインディングが成功するとこのブロックが動きます。ビックリマークを使うのはよくありません。この例では変換結果を表示するために「actualNumber」がそのまま使われています。いいですね、問題ありません。 オプショナルバインディングの説明がしばらく続きますね。重要な機能であるため、ぜひ理解していただきたいと思います。この機能により、多くの場面でお世話になることが多いでしょう。
例えば、オプショナル型の変数にアクセスして、それが nil
でない場合には、その値を使用するための新しい変数や定数を作成することができます。この際に、オリジナルの変数や定数を参照する必要がない場合には、新しい変数や定数に同じ名前を使用することが可能です。これは「シャドウイング」と呼ばれるテクニックです。
少し具体的な例を挙げて説明しましょう。例えば、value
という変数があるとしましょう。そして、その値を使いたいとします。この場合、以下のように書くことができます。
if let value = value {
print(value)
}
このように、オプショナルバインディングを使用して、オプショナルをアンラップした後、そのアンラップされた新しい変数にも同じ名前 value
を使うことができるのです。この手法を用いることで、元の変数に再度アクセスする必要がなくなり、コードの可読性が向上します。
なぜこれが便利かというと、このブロック内での value
が元の value
をアンラップしたものであるという関連性が明確になるからです。この関連性があることによって、コードの理解がしやすくなります。
オプショナル変数名について、それがオプショナルだと分かるような名前をつけた方が良いのかどうかについても考えたことがあります。しかし、変数がオプショナル型であるかどうかは、実際のところそこまで重要ではない場合があります。必要な時にだけオプショナルであることが示されれば良いという場面が多いでしょう。
シャドウイングによって、同じ名前の変数を使うことのメリットや、余計な名前を考えるコストが削減されることについて話しました。この仕組みは多くのプログラミング言語に備わっていますが、シャドウイングがない言語も存在します。そのような言語では、名前の付け方が厄介になりがちです。したがって、シャドウイングはとても便利な機能と言えます。 個人的にはとても好みな仕様ですね。よくできていますよね、これ。最近、リリース候補版がXcodeに登場しましたが、今使っているXcodeはそれではありません。新しいSwift 5.7だと、省略してシャドウイングなどを自動でやってくれるようになりました。つまり、ファインディングとシャドウイングをさらに強力な省略表記で行う構文です。今はまだ馴染みきれていませんが、これが当たり前に浸透すれば、良い感じのコードになると思います。
シャドウイングについてもう少し見ていきましょう。この勉強会でもすでに話したことがありますが、他にもシャドウイングが見られる場所があります。例えば、関数の定義のときにバリュー
としてint
型を受け取るようにして、print(value)
とすると、これはシャドウイングと言えますかね。あるスコープ内で、同じ名前の変数を宣言すると、そのスコープ内では新たな変数が元の変数を「隠す(shadow)」ことがシャドウイングです。
たとえば、ある関数でパラメーターとしてvalue
を受け取って、さらに内部でvalue
としてint?
(オプショナルint)を使うこともできます。ここでprintされるのはオプショナル型のvalue
になります。これはシャドウイングの基本的な例で、同じ名前の変数を使って元の値とは無関係に新たな変数を宣言できることを示しています。
ただし、この方法は注意が必要です。同じ名前をつけることで人が理解しにくくなる場合があります。例えば、value
をString
型から整数に変換する場合、変数名としては少しやり過ぎかなと感じるかもしれませんが、シャドウイングを利用して適切な形に置き換えて使うという方法もあります。
さらに、ローカルスコープ内でも例えばdo
ブロックがある場合は、その中で再度シャドウイングすることも可能です。こうすることで、パラメーターをシャドウイングし、さらにローカル変数をシャドウイングする、といった応用ができます。これは非常に強力な機能であり、さまざまな場面で役立ちます。
スライドの補足として、内包する値にアクセスした後で元の変数や定数を参照する必要がない場合にシャドウイングを利用できる、という話もありました。例えば、変数がもし値を持っていた場合にその値を使い終わった後で元の変数にnil
を代入するといった場合、シャドウイングを利用することができます。このようなケースは、他のプログラミング言語でもよく見られるものです。
以上が今回の内容です。シャドウイングの使い方について理解を深めることで、コードの可読性やメンテナンス性を向上させることができますので、ぜひ活用してみてください。 とりあえず、Flutterだと何かできるっぽいです。こういう書き方が良いのかどうかですが、例えば、シャドウイングが済んでしまっている場合、このバリューはオプショナル型ではないので、nil
を入れようとするとエラーになります。そのため、もしオプショナルなバリューを扱いたい場合、シャドウイングは避け、アンラップしたバリューとして変数を保持する方法を取ると良いです。表示が終わったあとは元の変数にnil
を入れる、こういう書き方になる訳です。
元の変数やその変数にアクセスする必要がない場合、シャドウイングの特徴を知っていれば事前に回避できます。シャドウイングをしちゃまずいケースもあるので注意が必要です。
また、シャドウイングという言葉は使わないことが多いですが、オブジェクト指向の場合も似たように扱われます。例えば、バリューとしてint
のオプショナル型があり、メソッドとして何かがあって、それをif let value = value
としてprint(value)
する場合、バリューにnil
を入れたいとします。この例では、シャドウイングを避けてみるアイデアもあります。これでビルドが通れば、実際に動作すると思いますが、これは一つの方法です。
もう一つの方法として、ここでシャドウイングをかけつつ、self.
を使ってプロパティを書き換える方法もあります。オブジェクトの定義が含まれる場合、元の値にアクセスする方法もあります。例えば、自分自身の所有しているバリューと、ローカルのバリューとは別の自分自身のバリューを区別する手段があるので、シャドウイングを使ってスマートに変数アクセスを行えます。
このように、シャドウイングを使うかどうかを考慮することで、適切な実装ができます。
さっきのオブジェクトの例を紹介しましたが、フリーな関数やフリーなプロパティについては名前空間の問題があります。自分自身のモジュールが名前空間となるのですが、プレイグラウンドでは名前空間がわかりにくいかもしれません。プロジェクトだとどうなるかを試してみたいですね。
自分自身のパーミナルアップで何かできるのか、そのアプリケーションの名前やバリューを保持する方法を試してみたいです。例えば、var value: Int = ...
のようにして、print(TerminalApp.value)
として実行できます。ビルドが通れば、ランタイム上で正しく実行されるか確認する必要があります。
このように、色々と試して理解を深めていくことが大切ですね。 仮にできるとすると、これがオプショナルになったとして、if let value = value
でプリントを行うとします。さっきの実行で言った通り、こうやってvalue = nil
とすると失敗するはずです。まず、こうしたときにここがダメですよってなります。ここに名前空間がないのでですね、これで、だからこのとき、こっちにパーミナルアップのバリューオブジェクトを取って実行すると、出てきますね。値が表示されてnil
になったよって感じですね。
なので、シャドウイングしようとしても名前空間でアクセスできるものであれば元の値を使えるから、安心してどんどんシャドウイングを使っていっていいです。でも、これがローカルスコープとかになると、このあたりでシャドウイングしちゃったりすると、名前空間でアクセスできるバリューは表示できますが、こちらのバリューについては取りようがないんです。このあたりに名前空間をつける方法ってあるんですかね? ないですよね。そうすると、多分ですけど、中途半端な空間で元のアクセスができなくなってしまうんです。もし扱う必要があるなら、シャドウイングは注意しないといけません。
他にもガードを使ったときに元のアクセスが必要になる場合もあるかな。あるいは、関数でエラーがパラメータとして渡されて、外部のところでエラーの中からさらにネストされているエラーの定義を取り出すときに、オプショナルバインディングができなかったときに、オリジナルのエラーを何か処理したいときがありますね。例えば、関数で1行目のパラメータがオプショナルだったんだけど、エラーからオプショナルが出ないエラーから中身を取ってくることを4行目で行って、6行目でそうじゃない場合は2行目のオリジナルエラーを使いたいときなどですね。
なるほど、その場合はシャドウイングが問題になる可能性がありますね。このガードの部分もそうですね。例えば、エラーをシャドウイングして別のエラーを定義してしまうと、元のエラーにアクセスできなくなることがあります。しかし、リリース候補版であれば問題ないかもしれませんね。
このシャドウイングの機能は便利ですが、使用する際には元の変数へアクセスできなくなることに注意が必要です。特に複雑な関数やエラーハンドリングの場面では、この点に気をつけてください。
今日は時間になったので、この辺りで終わりにしましょう。お疲れ様でした。ありがとうございました。