Swift deferの正しい使い方|リソース解放と後片付けを初心者向けに徹底解説
生徒
「先生、Swiftでdeferっていうキーワードを見たんですけど、どういうときに使うんですか?」
先生
「いい質問ですね。deferは、プログラムの処理が終わったあとに必ず実行してほしい処理をまとめて書くための仕組みなんです。例えば、ファイルを閉じたりメモリを解放したりといった“後片付け”に使われます。」
生徒
「なるほど、片付けを忘れないようにするための仕組みなんですね。具体的にどんな感じで書くんですか?」
先生
「それでは実際の使い方を一緒に見ていきましょう!」
1. Swiftのdeferとは?
deferは、Swiftに用意されているキーワードで、「この処理が終わるときに、必ずこれを実行してほしい」という後片付け専用の処理を書ける仕組みです。ポイントは、途中でreturnしたり、エラーが発生しても必ず実行されるところにあります。プログラムでは、使い終わったファイルや通信、仮のデータなどをきちんと片付けないと、動作不良やメモリ不足の原因になります。
日常生活にたとえると、「作業の途中で電話が来て席を立っても、最後には必ず机を片付ける」と決めておくようなものです。deferを使えば、処理の流れを意識しなくても、後片付けを自動で任せられます。
func simpleDeferExample() {
print("処理スタート")
defer {
print("必ず実行される後片付け")
}
print("途中の処理")
}
simpleDeferExample()
このサンプルでは、deferの中に書いた処理が、関数の最後に必ず実行されます。書いた位置に関係なく、スコープ({ })を抜ける直前に動くのが特徴です。初心者のうちは「最後に必ず動く予約処理」と覚えておくと、deferの役割をイメージしやすくなります。
2. deferの基本的な使い方
deferの基本ルールはシンプルで、同じスコープ(波括弧 { } の中)を抜ける直前に実行されるというものです。「deferを書いた場所」ではなく、「スコープが終わるタイミング」で動くのがポイントです。だから、途中で処理が終わっても(returnで抜けても)後片付けだけは最後に必ず走ります。
まずは、関数の中でdeferがどう動くかを見てみましょう。下のコードでは、メインの処理が終わったあとにdeferの中身が実行されます。
func sampleFunction() {
print("処理開始")
defer {
print("後片付け処理")
}
print("メインの処理中")
}
sampleFunction()
このコードを実行すると、出力結果は次のようになります。
処理開始
メインの処理中
後片付け処理
「最後に出るのがdefer」と覚えると分かりやすいです。もう少しイメージを強くするために、途中でreturnする例も見てみます。returnで早めに関数を終えても、deferはスキップされません。
func earlyReturnExample(isOK: Bool) {
print("チェック開始")
defer {
print("必ず実行される後片付け")
}
if !isOK {
print("条件に合わないので終了")
return
}
print("条件に合ったので続行")
}
earlyReturnExample(isOK: false)
このように、deferは「最後に必ず実行したい処理」を1か所にまとめられるので、片付け忘れを防ぎやすくなります。初心者のうちは、“スコープ終了の直前に動く予約”と捉えると理解しやすいです。
3. 複数のdeferがある場合
もしdeferを複数書いた場合は、最後に書いたものから順番に実行されます。これは「後入れ先出し(LIFO)」と呼ばれるルールです。
func multipleDefer() {
defer { print("1番目の後片付け") }
defer { print("2番目の後片付け") }
defer { print("3番目の後片付け") }
print("メインの処理実行中")
}
multipleDefer()
実行結果は次の通りです。
メインの処理実行中
3番目の後片付け
2番目の後片付け
1番目の後片付け
このように、最後に宣言したdeferから順番に実行されます。
4. ファイル操作でのdefer活用
deferは特に「リソース解放」に役立ちます。例えば、ファイルを開いたら必ず閉じる必要があります。閉じ忘れるとファイルがロックされたままになり、他の処理ができなくなることもあります。
func readFile() {
let file = "sample.txt"
print("\(file) を開きました")
defer {
print("\(file) を閉じました")
}
print("ファイルの内容を読み込んでいます...")
}
readFile()
このように、ファイル操作の最後に必ず実行したい処理をdeferに書いておけば、うっかり閉じ忘れる心配がなくなります。
5. ネットワークやメモリ解放でも役立つ
deferはファイル操作だけでなく、ネットワーク接続を切断する処理や、一時的に確保したメモリを解放する処理などにも使えます。
例えば、インターネットに接続してサーバーからデータを取得するプログラムを書くとき、接続を閉じるのを忘れると通信が無駄に残ってしまいます。しかし、deferを使えば確実に切断処理を実行できます。
このようにdeferは、プログラミングで避けられない「片付け」を忘れないための保険のような存在です。
6. エラー処理とdeferの関係
Swiftでは、エラーが発生すると処理の途中で関数を抜けてしまうことがあります。こうしたときもdeferに書いた処理は必ず実行されます。
enum SampleError: Error {
case somethingWrong
}
func errorHandlingExample() throws {
print("処理開始")
defer {
print("必ず実行される後片付け")
}
throw SampleError.somethingWrong
}
do {
try errorHandlingExample()
} catch {
print("エラーが発生しました: \(error)")
}
実行すると、エラーで処理が中断されてもdeferの内容はしっかり実行されます。これにより、プログラムが異常終了してもリソースを安全に解放できるのです。
7. deferを使うときの注意点
deferは便利ですが、使いすぎるとコードが読みにくくなることもあります。特に複数のdeferを重ねすぎると、実行順序を理解しづらくなります。
また、deferの中で時間がかかる処理をすると、関数の終了が遅れる原因にもなるため注意が必要です。基本的には「片付け」や「解放」のように短い処理だけを書くのがおすすめです。