3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめてのアドベントカレンダーAdvent Calendar 2023

Day 24

引数のクロージャ〜@escapingについて理解しよう!〜

Last updated at Posted at 2023-12-25

引数のクロージャ

クロージャを関数の引数や別のクロージャの引数として利用する場合には、属性とトレイリングクロージャが有効です。
属性はクロージャに対して指定する情報を追加するために使用する。
トレイリングクロージャはクロージャを引数に取る関数の可読性を高めるために使用する。

今回は属性である@escapingについて理解を深めていきたいと思います!

escaping属性とは

関数に引数として渡されたクロージャが、関数スコープ外で保持される可能性があることを示す属性。クロージャが関数のスコープ外で保持されなければ、クロージャの実行は関数の実行中に限られるので、キャプチャは必要ない。
escaping属性が必要な場合は、クロージャの実行時まで関数のスコープ変数を保持する必要があるため、キャプチャが必要。

escapingを使用する必要のない引数のクロージャ

escapingを使用する必要のない例から見てみましょう!

struct S {
    func f1(closure: () -> Void) {
        closure()
    }
}
print("before")
S().f1(closure: {
    print("in closure")
})
print("aftter")

func f1(closure: () -> Void)メソッドに渡された引数をメソッドをリターンするまでに呼ぶか、呼ばないかのときはescapingいらない。値を保持する必要がないため。
出力

before
in closure
aftter

escapingを使用する必要なある引数のクロージャ

class S {
    var closure: () -> Void = {}
    func f1(closure: @escaping() -> Void) {
//🟥後で呼ぶために保存すると、escapingつけないとエラーになる
        self.closure = closure
    }

    func f2() {
//プロパティに保存したクロージャを呼ぶ
        self.closure()
    }
}

print("before")
let s = S()
//プロパティをクロージャに保存
s.f1(closure: {
    print("in closure")
})
print("aftter")
//実行
s.f2()

func f1(closure: @escaping() -> Void)メソッドで、引数のクロージャを呼ばず、var closureに保持して、後で関数外で実行するためには@escapingが必要!

escapingを使用する実例

API通信でescapingクロージャを引数としてもつ下記のメソッドを使用しました!
dataTask(with:completionHandler:)

func dataTask(
    with request: URLRequest,
    completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void
) -> URLSessionDataTask

URLを元にHTTPレスポンスを取得するメソッド

async(group:qos:flags:execute:)

@preconcurrency
func async(
    group: DispatchGroup? = nil,
    qos: DispatchQoS = .unspecified,
    flags: DispatchWorkItemFlags = [],
    execute work: @escaping @Sendable () -> Void
)

dataTask(with:completionHandler:)
async(group:qos:flags:execute:)のような引数としてescapingクロージャを持つクロージャ内ではnon-escapingのクロージャをキャプチャできない。(保存できない)

func dataTask(completion: @escaping (String) -> Void) {
    
    DispatchQueue.global().async {
        //escaping内で呼ぶ場合、escaping必要
        //クロージャにキャプチャされ保存されている。
        completion("晴れ")
    }
}

func fetchWeather(completion: @escaping (String) -> Void) {
    //completionエスケーピングクロージャ
    dataTask(completion: { weather in
        completion(weather)
    })
}

escaping内で呼ぶ場合、dataTaskクロージャにキャプチャされ、(保存され)、escaping必要!
func fetchWeather(completion: @escaping (String) -> Void)には@escapingをつける必要がある!

参考文献

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?