defer 演算子は、現在のスコープを終了するまでコードの実行を遅延させます。通常は関数からの戻り時に実行されます。リソースのクリーンアップ、ファイルの閉鎖、およびメモリの解放などに便利で、いわゆる自動的なクリーンアップです。
特長:
例:
func processFile() { let file = openFile() defer { closeFile(file) } // ファイルの操作 // throw や return が実行された場合でも file は閉じられます }
典型外の使用: 'finally' タイプの構文の部分的な模倣、ログ記録や実行時間の追跡のためのコードのグルーピングなどです。
アプリケーションが関数の実行中に終了した場合、defer は実行されますか?
— いいえ。defer は、スコープの正常終了時(return、throw または正常な退出)のみ実行されます。アプリケーションが強制終了(たとえば、SIGKILL信号やfatalErrorによる)した場合、deferブロックは実行されません。
例:
func foo() { defer { print("クリーンアップ") } fatalError("クラッシュ!") // defer は実行されません }
物語
ソケットを扱う関数が、throwの場合に接続をクリーンアップしませんでした。データが残り、接続が解放されませんでした。deferを追加した後、すべてが正常に動作しました:リソースは常に閉じられていました。
物語
開発者は、グローバルステートの解放のためにdeferに頼ろうとしました。fatalErrorが発生した場合、リソースが解放されず、サービスがブロックされ、再起動が必要になりました。
物語
関数でいくつかのdeferを宣言し、宣言順で実行されると考えました。その結果、リソースが誤った順序で閉じられました(例えば、最初にファイルディスクリプタが閉じられ、その後それにアクセスしようとしました)。解決策:deferブロックのLIFO順序を忘れないことです。