💭

@escapingとは何か?

に公開

結論

  • 修飾子でクロージャの引数につけます。
  • クロージャが関数終了後も保持されて実行される可能性がある場合に必要

@escaping がしてくれていること

  • 通常のクロージャは「関数スコープ内で終わる」のでスタックに置かれて関数終了と同時に破棄される
  • @escapingをつけるとSwiftはクロージャをヒープ領域にコピーして保持する。
  • だから関数がreturnした後でも消えずに使える。
  • Swiftはメモリの安全性を強制する言語です。
    だから「このクロージャは関数の外に出て、関数が終わった後でも生き続けるよ」と宣言しないと勝手にスコープ外で呼べない仕組みになっています。

使用例

  1. 非同期処理の completion

関数が return したあとに completion が呼ばれるから @escaping が必要。

func fetchData(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        completion("結果")  // 関数が終わった後に呼ばれる
    }
}
  1. クロージャをプロパティに保存する時

関数を抜けてもクロージャが残るので escape します。

class Downloader {
    var onFinished: (() -> Void)?   // プロパティに保持(関数スコープを飛び出す)
    
    func download(completion: @escaping () -> Void) {
        self.onFinished = completion
    }
}

@escapingが不要な例

例1: 即時呼び出し

func greet(name: String, completion: (String) -> Void) {
    let message = "Hello, \(name)!"
    completion(message)   // 関数内ですぐ呼ぶ
}

greet(name: "Taro") { result in
    print(result)  // Hello, Taro!
}

例2: 複数回呼んでも関数内で完結

func repeatTwice(completion: (String) -> Void) {
    completion("First")
    completion("Second")
}

repeatTwice { text in
    print(text)
}

いかがでしたでしょうか?

記事内容に間違いや不明点などあればぜひ教えてください 🙏

Discussion