Swift・iOS

Swiftを中心に学んだことを記録に残すブログです。技術に関係ない記事もたまに書いています。

【Swift】escaping属性(@escaping)とは

クロージャを使用する時に出てくるescaping属性に関するメモ。

 

■escaping属性とは

以下、Appleの「Guides and Sample Code」のサイトの引用です。

ブラウザの翻訳機能だけではいまいち理解しにくいです。。

 

「A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

One way that a closure can escape is by being stored in a variable that is defined outside the function. As an example, many functions that start an asynchronous operation take a closure argument as a completion handler. The function returns after it starts the operation, but the closure isn’t called until the operation is completed—the closure needs to escape, to be called later. For example:

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

The someFunctionWithEscapingClosure(_:) function takes a closure as its argument and adds it to an array that’s declared outside the function. If you didn’t mark the parameter of this function with @escaping, you would get a compile-time error.」

 

訳:

クロージャは、クロージャが関数の引数として渡されたときに関数をエスケープすると言われますが、関数が返った後に呼び出されます。クロージャをパラメータの1つとして使用する関数を宣言する@escapingと、クロージャエスケープできることを示すためにパラメータの型の前に書き込むことができます。

クロージャエスケープできる1つの方法は、関数の外で定義された変数に格納することです。たとえば、非同期操作を開始する関数の多くは、完了ハンドラとしてクロージャ引数をとります。この関数は操作を開始した後に戻りますが、操作が完了するまでクロージャは呼び出されません。クロージャエスケープする必要があり、後で呼び出される必要があります。例えば:

〜コード省略〜

このsomeFunctionWithEscapingClosure(_:)関数は、引数としてクロージャをとり、それを関数外で宣言された配列に追加します。この関数のパラメータに印を付けていないと@escaping、コンパイル時にエラーが発生します。」

 

引用:The Swift Programming Language (Swift 4.1): Closures

 

※第2段落目がわかっていない

Appleの「Guides and Sample Code」の内容を完全に理解できていません。

第2段落目に関して、現状は以下の認識です。

(1)@escapingをつけないと、以下の手順で操作が完了する。 

1.関数においてクロージャ以外の処理を開始

2.クロージャ以外の処理が完了したらクロージャの処理を開始

3.クロージャの処理が完了

 

(2)@escapingをつけて関数外(スコープ外)で宣言された配列に入れる上記の例では以下の流れで操作が完了する。

1.関数においてクロージャ以外の処理とクロージャの処理が同時に開始

2.クロージャ以外の処理とクロージャの処理が完了

 

■わからないままでは終われない

以下、Appleの「Guides and Sample Code」のサイト外で調べたescaping属性に関するまとめです。

 

@escapingをつけると、関数に引数として渡されたクロージャが関数のスコープ外で保持され、逆に@escapingつけないと保持されません。@escapingは、クロージャが参照している定数や変数がローカルスコープ外にあっても参照を保持する「キャプチャ」が必要かを判別するものだからです。

そのため、クロージャの実行が関数のスコープ内でのみ行われる場合は@escapingはいりません。

 

■最後に

Appleの「Guides and Sample Code」サイト外で調べた内容は理解できたものの、公式の内容理解が完全ではないためなんとも気持ちが悪いです。実際にescaping属性を使う場面に出会う回数がまだ少ないため、もっとコードを書いて理解を深めないといけないなと思いました。くやしい。

 

■参考

The Swift Programming Language (Swift 4.1): Closures

引数のクロージャの属性【Swift実践入門読書メモ】 - ドン・エンジニーア

Swift 3 の @escaping とは何か - Qiita