[Swift] defer 文の使い方

Swift

Swift の defer 文の使い方を説明します。あまり知られていないかもしれませんが、非常に便利な statement です。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Big Sur 11.4
  • Xcode 12.5
  • iOS 14.5

defer

defer を使うと (defer が記述されている)ブロックのスコープを抜ける時に実行するコードブロックを定義することができます。

Swift のドキュメントは、こちら

defer の動作

動かしてみると、具体的な動作がわかりやすいです。

defer が1つのとき


func f() {
    defer { print("First defer") }  // (1)
    print("End of function")        // (2)
}
f()
// print-out
End of function
First defer

(2) の文を実行した後、関数 f のスコープを離脱する時に、(1) が実行されていることがわかります。

defer が複数の時

1つのブロックの中で、複数の defer 文を記述することができます。その時には、最後に記述された defer のコードブロックから逆順に実行されます。


func f() {
    defer { print("First defer") }
    defer { print("Second defer") }
    print("End of function")
}
f()
// print-out
End of function
Second defer
First defer

defer が実行されないケース

defer は、defer 文が実行されないと、defer 文で定義したコードブロックをスコープ離脱時に実行しません。


func f() {
    defer { print("First defer") }   // (1)
    if true { return }
    defer { print("Second defer") }  // (2)
    print("End of function")
}
f()
// print-out
First defer

(2) の文は実行されていないため、関数のスコープを離脱しても、実行されません。

関数 と コードブロック

defer の対象とするスコープは、関数に限ったものではありません。
if 文や for 文等の コードブロックに対しても機能します。


func f() {
    defer { print("First defer") }
    if true {
        defer{ print("defer from if-block")} // (1)
        print("if-true block")                    // (2)
    }
    defer { print("Second defer") }
    print("End of function")
}
f()
// print-out
if-true block
defer from if-block
End of function
Second defer
First defer

if ブロックの中で記述した defer ブロック(1) は、if ブロックの中の処理(2) の後に、実行されます。

defer の使いどころ

defer が便利に使えるケースとして、リソース等の管理があります。

ファイルを開いて処理してファイルを閉じる機能を考えてみます。


func ファイルを開いて処理して閉じる関数() {
   ファイルを開く処理
   ファイルを処理
   ファイルを閉じる処理
}

上記はシンプルに見えますが、通常 様々なエラー処理が必要となります。ファイルが開けなかった時、ファイルの処理がうまくいかなかった時 等々。

エラーが発生した時には、関数を抜けると思いますが、そこまでの処理でファイルを開いていたならば閉じて戻ることが必要です。
ですので、以下のように少し複雑になります。


func ファイルを開いて処理して閉じる関数() {
   ファイルを開く処理
   if (ファイル開けなかった) { return }
   ファイルを処理
   if (処理でエラー) { ファイル閉じる; return }
   ファイルを閉じる処理
}

エラーが発生した時にファイルを閉じる処理ことは、忘れやすいことでもあります。特に、関数の処理が複雑になってくると、ファイル以外にもさまざまな条件がでてくるので、より複雑になりがちです。
そのような時に、必要になった時点で defer を使って、後処理を記述しておくと、忘れないですし、コード的にもすっきりさせることができます。

defer を使うと以下のように記述できるようになり、すっきりします


func ファイルを開いて処理して閉じる関数() {
   ファイルを開く処理
   if (ファイル開けなかった) { return }
   defer { ファイルを閉じる処理 }
   ファイルを処理
   if (処理でエラー) { return }
}

スコープを抜ける時に、ファイルを閉じる処理が行われることは Swift が保証してくれます。

ファイルを処理するステップが複数入っても、ファイルを閉じ忘れることを気にしなくてよくなります。

まとめ:後処理には defer が便利

後処理には defer が便利
  • defer を使うと、スコープ離脱時の処理が記述できる
  • リソース管理に defer は、便利に使える
  • 忘れがちな後処理を defer にしておくと、忘れずにすむ

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です