问题の経緯:
deferはSwiftにおいてリソース管理やコードブロックの終了時に操作を行うための手段として導入されました。他の言語のfinallyのような役割を果たします。この構文はリソースのクリーンアップや終了処理のステップを明示的に示すのに役立ちます。
問題:
多くの初心者は、deferがすぐにネストされたコードを実行すると考え、トリガーのタイミングを正しく理解していないことがあります。また、複数のdeferブロックがある場合には、LIFO(Last In, First Out)のルールを知らなければ理解が難しい場合があります。try/catchや早期退出に関する複雑さもあります。
解決策:
deferはスコープの最後、現在の関数ブロックから出る前にネストされたコードを実行します。たとえ関数が早期にreturnしたり、throw、breakなどで退出した場合でも、deferは実行されます。複数のdeferは出現した順序の逆で実行されます。
コードの例:
func readFile() { print("ファイルを開く") defer { print("ファイルを閉じる") } print("行 1 を読む") if Bool.random() { print("早期リターン!") return } print("行 2 を読む") } readFile() // コンソールには常に: ファイルを開く, ..., ファイルを閉じる (最後に)
主な特徴:
deferを関数の外で使うことはできますか?
いいえ、deferは関数、メソッド、イニシャライザー、デイニシャライザーの内部でのみ許可されています。たとえば、ファイルのグローバルスペースや関数の外でコードソースの中に配置することはできません。
ブロック内で例外(throw)が発生した場合、deferはどうなりますか?
deferはそれでも実行されます。これがdeferの利点の一つであり、エラー(throw)が発生した場合でもリソースが確実に解放されます。これにより、安全なリソースの解放のパターンが実現されます。
複数のdeferはどのような順序で実行されますか?
それらは逆の順序(LIFO)で実行されます。後に宣言されたdeferが先に実行されます。
コードの例:
func test() { defer { print("最初") } defer { print("第二") } print("内部") } test() // 出力: "内部", "第二", "最初"
複数のdeferが不整列に配置されている関数。リソースのクリーンアップを忘れ、さまざまな場所から関数を戻ることにより、リソースのリークや動作のデバッグが難しくなります。
長所: コードの一貫性、クリーンアップアクションが1箇所にまとめられています。
短所: 何が実行され、どの順序で行われるかを追跡するのが難しく、ロジックのエラーによるリークが可能です。
重要な各ステージに対して1つのdeferを作成し、リソースがイニシャライズされる場所に直接配置し、コメントをつけます。コードレビューを行います。
長所: リソースの解放が保証され、行動の順序が明確に理解できます。
短所: 新しいリソースやクリーンアップブロックを追加する際には規律と注意が必要です。