はじめに
この記事は社内の勉強会で使用したものです。
Swift案件の経験が少ない社員や言語研修済みの新卒向けにNull安全について紹介したものです。
そのため、細かいOptional型の使い方というよりは「何が良いのか」「どういう風に運用していくのか」といった内容になります。
意見等いただければと思い、投稿致しました。
また、資料を作りにあたりいくつかの記事を参考にさせていただきました。ありがとうございました。
Null安全とは
Nullを上手に扱うための仕組みのことです。
仕組みそのものについては言語により差異はありますが、今回はSwiftを例に紹介していきます。
Swiftでは安全にNullを扱うために、OptionalというNullを許容する型を使っています。
XcodeでOptional
と打ってCommand+クリック
をするとOptional
の使い方がコメントで記載されているので一度見てみることをおすすめします。
Optionalは、?
または!
で表します。
let hoge: String = “abc” // 非オプショナル(nullを格納出来ない)
let fuga: String? = “abc” // オプショナル(nullを格納出来る)
print(hoge) // abc
print(fuga) // Optional(abc)
このようにNullを許容する型としない型を別物のように扱うことによって、「Optionalではない = Nullは絶対に入っていない」変数を実現しています。
Null安全でない言語との違い
Null安全かそうでないかによって、実際にコードを書く時に考えることの量や質に違いがあります。
既存のプロジェクトの一部を改修する際の例を以下に挙げます。
Objective-C(Null安全ではない言語)の場合
- (void)insertData:(NSString *)text {
// なんか処理
}
・text
はNullかもしれない
・もしNullだったらどうするのか?
┗ Assertで落とす/成功失敗をBoolで返す/ダイアログを表示する...?
・もしnonnull
が付いていても
・Nullそのものは防げてもNullポインタは防げない
┗ nonnull
がついているだけで信じられますか?
Swift(Null安全言語)の場合
func insertData(text: String) {
// なんか処理
}
・text
はNullではない
┗ 幸せ
・もしtext: String?
だったら
・まずそもそもこのメソッドを作った時点でわざとOptional
にしているのだから考慮しているはず
Null安全の利点の一つとして、
OptionalというNullを許容する型があることで、Nullの場合を考慮せざるを得ないが挙げられます。
Null安全の良いところ
Nullを扱おうという意識が付く
例えば、引数で渡されたtext
をDBにInsertするメソッドを書く時
func insertText(_ text: String
ここまで書いたタイミングでNullを意識せざるを得ないです。
1)func insertText(_ text: String) {
と書いた場合
・そもそもtext
にはNullを入れられない
・このメソッドを呼ぶ側でNullではない状態にしなくてはならない
2)func insertText(_ text: String?) {
と書いた場合
・Optional
になっているためアンラップする必要がある
そこから先どうするかはプログラマ次第ですが、まず一度意識しなくてはならないというのはNull安全言語のメリットだと感じます。
コードから読み取れる情報が多い
func insertData(_ text: String) {
// なんか処理
}
「ここがTextをinsertしてるメソッドかー」
「textは非Optionalだな」
「ということは、メソッドを使う側でNullを弾いているのか」
- (void)insertData:(NSString *) {
// なんか処理
}
「ここがTextをinsertしてるメソッドかー」
「これNullチェックどこでしてるんだ?」
(メソッドの頭でNullチェックをしていなかった場合)
「とりあえずメソッドを使っているところを探すか・・・」
「💩」
Optional
によって型を見ただけでNullが来る/来ないを判断出来るのは大きなメリットです。
Null安全の悪いところ
全部Optional
にして適当にアンラップしても動いてしまいます。
結局書く人次第です。
Null安全を使いこなすために
Null判定位置の統一
例えば、API通信で取得した文字列をDBにinsertする場合
API通信のレスポンスは必ずしも望んだ形で来るとは限りません。
では、どこでその判断をするのか?
・レスポンス取得時
・取得したレスポンスをOptional
として画面に返してから
・insertメソッドを呼ぶ前
・insertメソッド内で判定して可否をBoolで返させる
統一するとみんな幸せです。
簡単にNullの可能性を握り潰さない
hoge?.fugaAction()
アンラップは、Swiftにおいて数少ないNullを恐れざるを得ない処理です。
読む人を安心させるアンラップを心がけましょう。
アンラップ位置の統一
func hoge() {
guard let unwrappedFuga1 = fuga1 else { return }
guard let unwrappedFuga2 = fuga2 else { return }
// なんか処理
guard let unwrappedFuga3 = fuga3 else { return }
guard let unwrappedFuga4 = fuga4 else { return }
// なんか処理
guard let unwrappedFuga5 = fuga5 else { return }
}
func hoge() {
guard let unwrappedFuga1 = fuga1 else { return }
guard let unwrappedFuga2 = fuga2 else { return }
guard let unwrappedFuga3 = fuga3 else { return }
guard let unwrappedFuga4 = fuga4 else { return }
guard let unwrappedFuga5 = fuga5 else { return }
// なんか処理
}
下の方が気持ちいいし分かりやすいです。
まとめ
Null安全の最大のメリットは、コードに想いを乗せやすいことにあります。
Null安全を活用して気持ちの伝わるコードを書きましょう。