post Image
null安全な言語でも、バグ検知を怠れば安心ではない

null安全な言語のSwift, Kotlinを使えば、

リプレイス前より、安全安心になります!

( ̄ー ̄) ドヤッ!


本当に?


前提条件

仕様がアプリによって違うため
ここでは、文字列fooがnilまたはnullだった場合、
バグである仕様と仮定にします。
主語は、Swift, Kotlinの場合です。
以下は、僕の個人的見解です。


Objective-C時代
nilにメッセージを送っても落ちないが
バグを検知できない

NSString *foo = nil;
NSLog(@"%lu", (unsigned long)[foo length]); // 0
// nilに対するメッセージ送信結果はnil(数値を返す場合には0)が返却される
// クラッシュしないが、バグが起きたことは検知できない

Java時代

String string = null;
System.out.println(string.isEmpty()); 
// コンパイル時チェックできない、
// 実行時にjava.lang.NullPointerException
// コンパイル時にはチェックできなくて、実行時にクラッシュ
// Javaの@Nullableアノテーションが徹底されていないと、
// JaString型でnullが返ってくることがある

Swift リプレイス悪い例1
Implicitly Unwrapped Optionalはnilが代入できるのに、入らない前提で使ってしまう

var foo: String! = "aaaaaa"
foo = nil
print(foo.isEmpty)
// コンパイル時にはチェックできなくて、実行時にクラッシュ
// Optional型だから、nilを代入できるので
// 決してnilが入らないことを保証するString型として使ってはならない

Kotlin リプレイス悪い例1
Javaから受け取ったPlatform typeをnullが入らない前提で使ってしまう

var foo = getJavaString() 
// String!型でnullが入っていた場合、
print(foo.isEmpty())
// コンパイル時にはチェックできず、実行時にnullポクラッシュ

Swift リプレイス悪い例2
Optionalチェーンで、バグを握り潰し検知をしない
Objective-Cと品質が変わっていないので意味ない

var foo: String? = nil
print(foo?.isEmpty) // nil 
print(foo?.isEmpty == false) // false 
// クラッシュしないがバグが起きたことを検知できない

Optinal Chain(?)でクラッシュしなくなるけど、

落ちないだけで、

バグが起きたこと検知してないと

バグの発見が遅れ、被害が大きくなる


Swift リプレイス悪い例2

var foo: String? = nil

if let foo = foo, !foo.isEmpty {
  print(foo)
} else {
  print("foo is nil or foo is empty")
// printはローカル端末しか出ないので、
// 本番でサーバーに送らないと、バグ検知できない
}

Swift リプレイス悪い例3

var foo: String? = nil

if let foo = foo, !foo.isEmpty {
  print(foo)
} else {
  fatalError("foo is nil or foo is empty")
// エラー検知できるが、本番でアプリが強制終了してしまう
}

全然安心じゃないじゃないか


Swift リプレイス 個人的改善案
Optionalをアンラップした場合やelseの場合で、バグが起きるケースでは、
本番時サーバーにバグを報告する
強制アンラップ(!)は使わない。


var foo: String? = nil   
if let foo = foo, !foo.isEmpty {
  print(foo)
} else {
// FIXME: メソッド外だし ファイル名 行数出力
#ifdef DEBUG  // FIXME: 関数に外だします
  fatalError("foo is nil or foo is empty") // 開発時
#else
  // FIXME: 本番はユーザーに適切なエラーメッセージを表示して、
  // サーバーにバグを報告する
  errorReport("foo is nil or foo is empty") 
#endif
}

Kotlin リプレイス 個人的改善案
Javaから受け取ったPlatform typeはOptional型にしてアンラップする
Optionalをアンラップする場合や、elseの場合で
バグがある場合は、サーバーにバグを報告
強制アンラップ(!!)は使わない。
ネストが深くなるなら、Swiftのguard式,if letを移植しましょう!


var foo: String? = getJavaString() 
foo?.let { foo ->
  println(foo.isEmtpy())
} ?: run { 
// FIXME: メソッド外だし ファイル名 行数出力 
  if (BuildConfig.DEBUG) {
    throw(Exception("foo is null")) // FIXME: 適切なExceptionにしてください
  } else {
    // FIXME: 本番はユーザーに適切なエラーメッセージを表示して、
    // サーバーにバグを報告する
    errorReport("foo is null") 
  }
}

メリット: 開発時は、fatalErrorで、すぐにバグを検知できる。
本番時は、クラッシュしないので、ユーザーに適切なエラーメッセージを伝えられて、
ロールバックもできるし、サーバーにバグを報告するので、すぐにバグを検知して直せる。
バグ検知のために、ファイル名や行数も一緒に送る


デメリット: 面倒?

バグ検知するコードを書く面倒より、
バグ検知するコードがなくて、
バグ検知が遅れて、被害が広がったり、
手探りでバグを見つける方がもっと面倒なので、
nil,nullに関わらず、バグが起きるときは、
バグ検知するコードを安心のために書きましょう!


『 Swift 』Article List