🐶

if let _ = hoge VS if hoge != nil について考える

2022/01/30に公開

はじめに

どうもこんにちは、TOSHです。普段はQiitaに記事を投稿することが多いのですが、せっかくなので、Zennでも記事を書いてみようと思い、初投稿記事になります。どっちのプラットフォームの方が書きやすいのか、閲覧数が多いのか気になるところですね。
よかったら、Qiitaの記事にも目を通してみてください。tosh_3

Swiftのnilチェック

Swiftのnilについてはまた別の機会に紹介しようと思うのですが、nilチェックを行う際に、よく見るコード大きく分けて、2通りなのではないでしょうか?

if let _ = hoge形式

if let _ = hoge {
    // Do something
}

if hoge != nil形式

if hoge != nil {
    // Do something
}

結果としてやっていることは、hogeという変数がnilかどうかをチェックして、nilでない場合には、動作を実行するというように、同じ挙動をしていると思うのですが、これらは実際にはどのような違いがあるのでしょうか?

実行レベルのチェック

ここについてどうと判断するのは難しいのですが、例えば、上記の動作を実行するファイルをそれぞれ使用して、
swiftc -O -emit-assembly Hoge.swift
のような形式で実行すると、どちらの形式も同じアセンブリが排出されます。

Lloh1:	Lloh1:
add x8, x8, _$s4main4hogeSiSgvp@PAGEOFF	add x8, x8, _$s4main4hogeSiSgvp@PAGEOFF
mov w9, #12	mov w9, #12
str x9, [x8]	str x9, [x8]
strb wzr, [x8, #8]	strb wzr, [x8, #8]
mov w0, #0	mov w0, #0
ret	ret

一方で、Xcode上で、viewDidLoadの中に入れてあげ、breakpointを打ち、always show Assemblyで比較してみると、実行結果は異なります。
if let _ = hoge

->  0x1004cbe54 <+36>: add    sp, sp, #0x20             ; =0x20 
    0x1004cbe58 <+40>: ret    

if hoge != nil

->  0x102cffe20 <+36>: str    xzr, [sp, #0x8]
    0x102cffe24 <+40>: and    w8, w8, w8
    0x102cffe28 <+44>: strb   w8, [sp, #0x10]
    0x102cffe2c <+48>: ldrb   w8, [sp, #0x10]
    0x102cffe30 <+52>: tbnz   w8, #0x0, 0x102cffe40     ; <+68> at ViewController.swift
    0x102cffe34 <+56>: mov    w8, #0x1
    0x102cffe38 <+60>: str    w8, [sp, #0x4]
    0x102cffe3c <+64>: b      0x102cffe48               ; <+76> at ViewController.swift
    0x102cffe40 <+68>: mov    w8, #0x0
    0x102cffe44 <+72>: str    w8, [sp, #0x4]
    0x102cffe48 <+76>: ldr    w8, [sp, #0x4]
    0x102cffe4c <+80>: tbz    w8, #0x0, 0x102cffe54     ; <+88> at ViewController.swift:18:5
    0x102cffe50 <+84>: b      0x102cffe54               ; <+88> at ViewController.swift:18:5
    0x102cffe54 <+88>: add    sp, sp, #0x30             ; =0x30 
    0x102cffe58 <+92>: ret    

どうもif hoge != nil形式の方が幾分か手数が多いように見えます。
とはいえ、この程度の命令の差で誤差の範囲に含まれると思われます。知らんけど。
なので、実行レベルではほぼ一緒であると考えて良いのではないでしょうか?

ではどちらを使用するべきなのか?

これについて良し悪しはあると思いますが、個人的には、if hoge != nil形式を推します。
というのも、パフォーマンスに差分がないとすれば、可読性が決め手になってくるのではないでしょうか?
if let _ = hogeというのは、wildcardである_を使用しており、ぱっと見何をしているのかがみやすいのかというととてもわかりやすいとは言えません。if hoge != nilの方が幾分かシンプルに見えやすいのではないでしょうか?
if let _ = hogeの方が玄人感がしてかっこいいのですが、読んだ際の思考の手筈は、

1. hogeがnilかどうかチェックしている
2. hogeがnon nilの場合の値を定義
3. 定義した値は使用する予定がないので、wildcardにしておく

と三手になるのに対し、if hoge != nilでは、

1. hogeがnilかどうかチェックしている

と一手で表現できるので、こちらの方がシンプルな気がしますよね。という観点から個人的にはif hoge != nilを推しています。

if let _ = hogeの方が好ましいケース

もし仮に既存のコードの中でif let _ = hogeが多用されているのであれば、多用されている方を使用するべきだと思います。
あとは、今はwildcardでセットしているが将来的に、その値を使用する可能性があるのではあれば保守性の観点からif let _ = hogeを使用するべきだと思います。

個人的には、

if let _ = hoge as? String {
  // Do something
}

みたいなケースでも、if let _ = hogeを使用する気がします。その方が綺麗に書けるので。

最後に

まあ、なので好みと言えば好みだと思います。

参考

https://stackoverflow.com/questions/29322977/whats-the-difference-between-if-nil-optional-and-if-let-optional

https://docs.swift.org/swift-book/ReferenceManual/Patterns.html#//apple_ref/swift/grammar/wildcard-pattern

Discussion