引数のクロージャ
クロージャを関数の引数や別のクロージャの引数として利用する場合には、属性とトレイリングクロージャが有効です。
属性はクロージャに対して指定する情報を追加するために使用する。
トレイリングクロージャはクロージャを引数に取る関数の可読性を高めるために使用する。
今回は属性である@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
をつける必要がある!
参考文献