deferは、Swiftの特別な構文で、関数のスコープを退出する直前に特定のコードブロックを実行できるようにします。これは通常のreturnまたはエラー(throw)でスコープから退出した場合に関係なく行われます。deferは、リソースの解放、変更の取り消し、または操作の最終化に便利です。
動作の特徴:
例:
func testDefer() { print("begin") defer { print("first defer") } defer { print("second defer") } print("end") } // 出力: // begin // end // second defer // first defer
境界条件:
fatalErrorを呼び出した場合、関数内のすべてのdeferブロックは実行されますか?
回答: いいえ、関数内でfatalError(またはそれに類似した制御不能なクラッシュ)が呼び出されると、すべてのdeferで遅延させたブロックは実行されません。deferはアプリケーションの異常終了時にコードを呼び出すことを保証しません。
例:
func foo() { defer { print("Defer 1") } fatalError("Oops") defer { print("Defer 2") } } foo() // 何も出力されず、クラッシュします
事例
プロジェクトでファイルディスクリプタを閉じるためにdeferを使用しました。エラーが発生した際に適切なthrowの代わりにfatalErrorを使用し、クラッシュ時にdeferが動作しないためにオープンリソースがリークしました。
事例
ある関数に複数のdeferがあり、そのうちいくつかはローカル変数の状態に依存していました。開発者は変数が特定の値であると期待していましたが、その値がdeferの中で他のコードの部分で変更され、deferの実行時に最新の値が使用され、間違ったIDによるトランザクション取り消しのバグを引き起こしました。
事例
ネストされたclosure内でdeferブロックを書き、これが外部関数から出るときに実行されると考えました。結果として、このdeferはclosureから出るときに実行され、関数全体から出るときではなく、リソースが早すぎて解放されてしまいました。