Overview 概要
Many of the APIs you use in Swift take a closure—or a function passed as an instance—as a parameter. Because closures can contain code that interacts with multiple parts of an app, it's important to understand the different ways closures can be called by the APIs you pass them to. Closures you pass to APIs can be called synchronously (immediately) or asynchronously (sometime later). They may be called once, many times, or never. あなたがSwiftで使うAPIの多くは、クロージャ — またはインスタンスとして渡される関数 — をパラメータとして取ります。クロージャはアプリの複数の部分と相互作用するコードを含むことが可能なため、クロージャが、あなたがそれを渡す相手であるAPIによって呼び出される様々に異なる方法を理解するのは重要です。あなたがAPIに渡すクロージャは、同期的に(直ちに)または非同期的に(いつか後で)呼び出されることができます。それらが呼び出されるのは、一度だけ、何度も、または決してないかもしれません。
Important 重要
Making false assumptions about when a closure is called can lead to data inconsistency and app crashes. いつクロージャが呼び出されるかについてfalseを仮定することは、データの矛盾そしてアプリクラッシュに至る可能性があります。
Understand the Results of Synchronous and Asynchronous Calls 同期および非同期呼び出しの結果を理解する
When you pass a closure to an API, consider when that closure will be called relative to the other code in your app. In synchronous APIs, the result of calling the closure will be available immediately after you pass the closure. In asynchronous APIs, the result won't be available until sometime later; this difference affects how you write code both in your closure as well as the code following your closure. あなたがクロージャをAPIに渡す場合、クロージャが呼び出されるのはあなたのアプリの他のコードと比較していつかを考えてください。同期APIでは、クロージャ呼び出しの結果は、あなたがクロージャを渡した直後に利用可能になります。非同期APIでは、結果はいつか後まで利用可能でないでしょう;この違いは、あなたがコードを書く方法に、あなたのクロージャにおいてそれだけでなくあなたのクロージャの後のコードの両方で影響します。
The example below defines two functions, now(_:)
and later(_:)
. You can call both functions the same way: with a trailing closure and no other arguments. Both now(_:)
and later(_:)
accept a closure and call it, but later(_:)
waits a couple seconds before calling its closure.
下の例は、2つの関数、now(_:)
とlater(_:)
を定義します。あなたは、両方の関数を同じ方法で呼び出せます:後付クロージャとともにそして他の引数なしで。now(_:)
とlater(_:)
の両方ともクロージャを受け取りそれを呼び出します、しかしlater(_:)
はそれのクロージャを呼び出す前に2秒ほど待ちます。
The now(_:)
and later(_:)
functions represent the two most common categories of APIs you'll encounter in methods from app frameworks that take closures: synchronous APIs like now(_:)
, and asynchronous APIs like later(_:)
.
now(_:)
とlater(_:)
関数は、クロージャを取るアプリフレームワーク由来のメソッドにおいてあなたが出くわす、2つの最も一般的なAPIのカテゴリを表します:now(_:)
のような同期的API、そしてlater(_:)
のような非同期的API。
Because calling a closure can change the local and global state of your app, the code you write on the lines after passing a closure needs to be written with a careful consideration of when that closure is called. Even something as simple as printing a sequence of letters can be affected by the timing of a closure call: クロージャ呼び出しはあなたのアプリのローカルおよびグローバルな状態を変える可能性があることから、クロージャを渡す後の行であなたが書くコードは、いつそのクロージャが呼び出されるか注意深く考えて書かれる必要があります。一連の文字を印字するような単純なことでさえ、クロージャ呼び出しの時機によって影響される可能性があります:
Running the code in the example above usually prints the letters in the order B
→ C
→ D
→ A
. Even though the line that prints A
is first in the code, it's ordered later in the output. The ordering difference happens due to the way the now(_:)
and later(_:)
functions are defined. You need to know how each function calls its closure if you write code that relies on a specific execution order.
上の例のコードを実行することは、通常は文字をこの順番で出力します、B
→ C
→ D
→ A
。たとえA
を印字する行がコードにおいて最初であっても、それは出力においてもっと後の順番にされます。順番の違いは、now(_:)
とlater(_:)
関数が定義される方法のために起こります。あなたは、各関数がそれのクロージャを呼び出す方法を知る必要があります、もしあなたがある明確な実行順序を当てにするコードを書くならば。
Note 注意
The order in which A
is printed relative to the other letters isn't guaranteed. Under typical system conditions, it's usually printed last, but you shouldn't write code that relies on the order of an asychronous call relative to synchronous code without performing more careful synchronization between threads.
A
が他の文字に関連して印字される順番は、保証されません。典型的なシステム条件の下、それはふつうは最後に印字されます、しかしあなたは、スレッド間でより注意深い同期を実行することなく、同期コードに関連して非同期呼び出しの順番を頼るコードを書くべきではありません。
You'll need to consider this kind of time-based execution problem frequently when using APIs that take closures. In many cases, only one sequence of calls is correct for your app, so it's important to think through what the state of your app will be, given the APIs you're using. Use API names and parameter names along with documentation to determine whether an API is synchronous or asynchronous. あなたは、クロージャを取るAPIを使う場合に、この種の時間基盤の実行問題をしばしば考える必要があるでしょう。多くの場合に、ただ1つの呼び出し順番だけがあなたのアプリにとって正しいです、それで重要なのは、あなたが使うAPIを与えられて、あなたのアプリの状態がどうなるのか考え抜くことです。API名とパラメータ名を、加えてAPIが同期的または非同期的かを明らかにする文書とともに使ってください。
A common timing mistake is expecting the results of an asynchronous call to be available within the calling synchronous code. For example, the later(_:)
method above is comparable to the URLSession
class's data
method, which is also asynchronous. A timing scenario you should avoid is calling the data
method within your app's view
method and attempting to use the results outside of the closure you pass as the completion handler.
よくあるタイミング間違いは、非同期呼び出しの結果が、呼び出している同期的コード内部で利用可能であると予想することです。例えば、上のlater(_:)
メソッドは、URLSession
クラスのdata
メソッドと似ています、それもまた非同期です。あなたが避けるべきタイミングシナリオは、data
メソッドをあなたのアプリのview
メソッド内部で呼び出す、そして結果をあなたが完了ハンドラとして渡したクロージャの外部で使うのを試みることです。
Don't Write Code That Makes a One-Time Change in a Closure That's Called Multiple Times 一回限りの変更をするコードを複数回呼び出されるクロージャの中に書かないでください
If you're going to pass a closure to an API that might call it multiple times, omit code that's intended to make a one-time change to external state. あなたがあるクロージャをそれを複数回呼び出すかもしれないAPIに渡そうとしているならば、一度限りの変更を外部状態に対してするように意図されるコードを省いてください。
The example below creates a File
and an array of data lines to write to the file that the handle refers to:
下の例は、File
と、データ行からなる配列を作成して、そのハンドルが参照するファイルに書き出します:
To write each line to the file, pass a closure to the for
method:
各行をファイルに書き出すには、クロージャをfor
メソッドに渡してください:
When you're finished using a File
, close it using close
. The correct placement of the call to close
is outside of the closure:
あなたがFile
の使用を完了した場合、それをclose
を使って閉じてください。close
への呼び出しの正しい配置は、クロージャの外側です:
If you misunderstand the requirements of close
, you might place the call inside the closure. Doing so crashes your app:
あなたがclose
の必要条件を取り違えるならば、あなたは呼び出しをクロージャの内側に置くかもしれません。そうすることはあなたのアプリをクラッシュさせます:
Don't Put Critical Code in a Closure That Might Not Be Called 呼び出されないかもしれないクロージャの中に重大なコードを置かないでください
If there's a chance that a closure you pass to an API won't be called, don't put code that's critical to continuing your app in the closure. あなたがAPIに渡すクロージャが呼び出されない可能性があるならば、あなたのアプリを継続するのに重要なコードをクロージャの中に置かないでください。
The example below defines a Lottery
enumeration that randomly picks a winning number and calls a completion handler if the right number is guessed:
下の例は、Lottery
列挙を定義します、それはランダムに当選番号を選んで、正しい数が推測されるならば完了ハンドラを呼び出します。
Writing code that depends on the completion handler being called is dangerous. There's no guarantee that the random guess will be correct, so important actions like paying bills—scheduled for after you win the lottery—might never happen. 呼び出される完了ハンドラに頼るコードを書くことは危険です。ランダムな推測が正しくなる保証はありません、それで請求書を払うような — あなたが宝くじに当たった後に予定される — 重要な行為は決して起こらないかもしれません。