Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

Swift辞典

  1. トップページ
  2. Swift辞典
  3. @escaping / @autoclosure / キャプチャリスト

@escaping / @autoclosure / キャプチャリスト

Swiftの『@escaping』は関数スコープを超えて生存するクロージャに付けるアノテーションです。『@autoclosure』は引数を自動的にクロージャ化し、キャプチャリストは循環参照を防ぎます。

構文
// @escaping: 関数外で保持・実行されるクロージャ
func 関数名(completion: @escaping () -> Void) {
    DispatchQueue.main.async {
        completion()  // 関数リターン後に呼ばれる
    }
}

// @autoclosure: 引数を自動的にクロージャ化
func 関数名(_ condition: @autoclosure () -> Bool) { }
関数名(x > 0)  // { x > 0 } と同等

// キャプチャリスト(循環参照防止)
{ [weak self, unowned obj] in
    self?.method()
}
構文一覧
構文概要
@escapingクロージャが関数の実行後も保持・呼び出されることを示します。
@autoclosure引数の式を自動的にクロージャに変換します。遅延評価が可能になります。
[weak self]selfを弱参照でキャプチャします。selfがnilになりうる場合に使います。
[unowned self]selfを非所有参照でキャプチャします。selfが必ず生きている場合に使います。
[weak obj]任意のオブジェクトを弱参照でキャプチャします。
self?.method()weak selfをアンラップしてメソッドを呼び出します。
サンプルコード
import Foundation

// @escaping の例(非同期処理のコールバック)
var completionHandlers: [() -> Void] = []

func addHandler(_ handler: @escaping () -> Void) {
    completionHandlers.append(handler)  // 関数スコープ外に保持
}

addHandler { print("ハンドラー1実行") }
addHandler { print("ハンドラー2実行") }
completionHandlers.forEach { $0() }

// @autoclosure の例(短絡評価を実現)
func logIf(_ condition: @autoclosure () -> Bool, message: String) {
    if condition() {
        print("[LOG] \(message)")
    }
}

let value = 42
logIf(value > 0, message: "正の値です")   // value > 0 がクロージャに
logIf(value < 0, message: "負の値です")   // 条件が偽なら評価されない

// キャプチャリストによる循環参照防止
class ViewController {
    var name = "メイン画面"

    func setupTimer() {
        // [weak self] で循環参照を防ぐ
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] _ in
            guard let self = self else { return }
            print("\(self.name) のタイマー発火")
        }
    }
}

// 値型のキャプチャ(コピーされる)
var counter = 0
let captureByValue = { [counter] in
    print("キャプチャ時の値: \(counter)")
}
counter = 100
captureByValue()  // キャプチャ時(0)の値を表示
print("現在の値: \(counter)")
概要

クロージャがプロパティとして保持される場合や非同期で呼ばれる場合は『@escaping』が必要です。付け忘れるとコンパイルエラーになります。

クラスのメソッド内でクロージャを使い、クロージャ内で『self』を参照する場合は循環参照に注意が必要です。『[weak self]』でキャプチャし、必ず『self?.』または『guard let self = self』でアンラップしてください。『[unowned self]』はselfが必ず生きている保証がある場合のみ使います。

クロージャの基本についてはクロージャ(基本)/ { } / inを参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。