2015/02/25

SwiftでassertとpreconditionとfatalErrorをうまく使い分ける

Swiftにはアサーション系のメソッドとして、次の5つのメソッドがあります。

これらの違いや使い分けについて簡単に紹介します。

Swiftには3つの最適化レベルがある

まず、アサーション系メソッドを説明するために必要な、Swiftの最適化レベルについて簡単に説明します。

コマンドラインでswiftc --helpを実行させてみるとわかる通り、Swiftコンパイラには3つの最適化レベルがあることがわかります。

$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc --help
OVERVIEW: Swift compiler
                  (中略)
OPTIONS:
  -Onone                  Compile without any optimization
  -O                      Compile with optimizations
  -Ounchecked             Compile with optimizations and remove runtime safety checks

上記の説明の通り、-Ounchecked-Oよりアグレッシブなオプションというわけです。

まとめると、最適化レベルは次のようになります。

  • -Ononeは最適化を行いません。Xcodeのデフォルト設定ではDebugビルドにこれが設定されています。
  • -Oは最適化を行います。Xcodeのデフォルト設定ではReleaseビルドにこれが設定されています。
  • -Ouncheckedは最適化を行い、さらに実行時の安全性チェックも消します。Xcodeのデフォルト設定では利用されていません。

アサーション系メソッドまとめ

簡単に言えば、アサーション系メソッドは次の2つの観点で分けられます。

  • (最適化レベルによって) 実行されるかどうか
  • 条件チェックがあるかどうか

というわけで、各メソッドが各ビルドでチェックが有効になるかどうかをまとめた表がこちら。

表中で○になっている部分は、実行時にチェックが有効になることを意味します。

例えば、assertはデバッグビルドでは有効になり、実行時にcondition部分の結果がfalseならアサーションエラーとなりますが、 リリースビルドや無チェックビルドでは無効になります (チェックが無効のときはconditionは常にtrueになります)。

assertcondition部分がなくて常にfalseなバージョンが右端assertionFailureです。 fatalErrorには常にfalseなバージョンはありません。

どう使い分けるか?

各メソッドの機能的な意味よりもメソッド名が持つ意味を重視して、うまく使い分けるのがよさそうです。

fatalErrorはXcodeを利用しているとコード補完などでたまにこんな感じで出されることがあります。呼ばれるはずのない未実装のメソッドや来るはずのないcaseなんかで利用するのがよさそうです。

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

GitHub内のSwiftコードを見る限り、やはりメソッド先頭で事前条件に使われていることが多いみたいです (Carthageでもちゃんと使われていますね)。

というわけで、コードの各所でここにはこういう値しか来ないよというのを表明 (assert) しつつ、関数やループブロックの先頭なんかで事前条件 (precondition) を書き、想定外のどうしようもないエラーはfatalErrorするみたいなのが想定された使いかたかなと思います。

関連リンク

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。