Swift2.0で追加されたdefer
について詳しく調べてみました。
defer
は下記事でも少し触れています。
deferとは
defer {…}
と書くと、そのブロックを抜ける時に{}
内の処理が走るという構文です。
下のように書いた場合、1が出力された後に2が出力されます。
func method() { defer { print(2) } print(1) } method() // 1 → 2の順番に順番に出力される
deferの使いどころ
ブロックの最後に呼ばれてほしい処理がある場合に使うと良さそうです。
ただしJavaのtry-catch-finallyと違い、defer
はcatch
の前に呼ばれるのでそこだけ注意が必要です。
enum MyError: ErrorType { case Error1 case Error2 } do { defer { print("defer") // 例外が起きても起きなくてもブロックを抜ける前に呼ばれてほしい } throw MyError.Error1 } catch { print("catch") } // defer → catch の順に呼ばれる
return
の後に呼ばれるので下のような使い方もできそうです。
class MyClass { var value: Int? = 1 func method() -> Int? { defer { value = nil } return value } } print(MyClass().method()) // → Optional(1)
deferはセットと逆順で呼ばれる
deferはセットした順番と逆順に呼ばれるという特徴があります。
下の形だとSTART → 1 → 2 → 3
と呼ばれそうなのですが、実は3,2,1
の順番で呼ばれます。
公式ドキュメントにも逆順で呼ばれる事が記載されてるので不具合ではなさそうです。
func method() { defer { print(1) } defer { print(2) } defer { print(3) } print("START") } method() // → 出力は START → 3 → 2 → 1 の順番
deferは入れ子もできる
下のようにdefer
を入れ子にする事もできます。
この場合START → 1 → 2
の順番に出力されます
func method() { defer { defer { print(2) } print(1) } print("START") } method() // → START → 1 → 2
deferのセット前にブロックを抜けると呼ばれない
下のように、defer
の前にreturn
するとprint(1)
は呼ばれません。
func method() { return defer { print(1) } } method()
別スレッドの処理の完了を待たない
下の場合は"普通の出力" → "defer" → "別スレッド"
という順番で呼ばれます。
func method() { defer { print("defer") } print("普通の出力") dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { NSThread.sleepForTimeInterval(1.0) print("別スレッド") }) } method()