ネットワークからのデータのダウンロードや画像処理などの時間のかかるコードを実行して
iOS
アプリケーションの
UI
応答性を実現する場合は、マルチスレッドに関連する高度なパターン(
oncurrency
)を使用する必要があります。そうでない場合は、ユーザーインターフェイス(
U
I)大幅に減速し始め、完全な「凍結」に至ることさえあります。
main thread
からリソースを消費するタスクを削除する必要があります。
main thread
は、ユーザーインターフェイス(
UI
)を表示するコードを実行します。
現在のバージョンの
Swift 3
および最も近い
Swift 4
(2017年秋)では、これは、
Swift
組み込み言語コンストラクトにまだ関連付けられていない2つの方法で実行できます。その実装は、
Swift 5
(2018年末)でのみ開始されます。
それらの1つは
GCD (Grand Central Dispatch)
おり、 前の記事はそれに専念しています。 この記事では、Operation操作や
Operation
操作キューなどの抽象的な概念を使用して、
iOS
アプリケーションで
UI
応答性を実現する方法を示します。 また、これら2つのアプローチの違いと、どの状況でどちらを使用するのが適切かを示します。
この記事のコードはGithubで見ることができます。
Operation
とは
Operation
適切な定義は、 NSHipsterに記載されています。
Operation
は完了したタスクであり、操作の状態、その優先度、他のOperations
からの依存関係をシミュレートし、この操作を管理するためのスレッドセーフ構造を提供する抽象クラスです。
基本的な概念。 Operation
最も単純な
Operation
は、通常のクロージャで表すことができ、これも
DispatchQueue
で実行できます。 ただし、この形式の操作は、
addOperation
メソッドを使用して
OperationQueue
追加する場合にのみ適用できます。
完全な操作操作は、
BlockOperation
イニシャ
BlockOperation
を使用して構築できます。 独自の
start ()
メソッドを使用して
start ()
ます。
同期関数の非同期バージョンのような再利用可能なものを取得したい場合、
Operation
クラスのカスタム
subclass
を作成し、そのインスタンスを取得する必要があります。
対応するフィルターを使用してぼやけた画像を取得する
FilterOperation
操作は、
Operation
クラスのユーザー
subclass
として定義されます。 ユーザークラスには、入力プロパティと出力プロパティの両方、および他のヘルパー関数を含めることができます。 操作の機能部分に対応するために、
main ()
メソッドを再定義(
override
)しました。
Operation
クラスを使用すると、
OperationQueue
操作キューで将来実行できるタスクを作成できますが、現時点では、他の
Operations
するまで待機できます。
Operation
には
state mashine
、これは操作
Operation
「ライフサイクル」です。
操作操作の可能な状態は、
pending
(保留)、
ready
(実行準備完了)、
executing
(実行済み)、
finished
(完了済み)、
cancelled
(破棄済み)です。
Operation
を作成して
Operation
に配置するとき、操作を
pending
設定します。 しばらくすると、
ready
状態になり、いつでも
executing
状態に移行して
OperationQueue
送信して実行できます。
executing
状態は、ミリ秒から数分以上続くことがあります。 完了後、
Operation
はfinal状態の
finished
進みます。 この単純な「ライフ」サイクルの任意の時点で、Operation操作は破棄され、
cancelled
状態になります。
Operation
クラス
API
は、
Operation
この「ライフサイクル」を反映しており、以下に示されています。
start()
メソッドを使用して、Operation操作を実行することができますが、ほとんどの場合、
OperationQueue
操作キューに操作を追加すると、このキューは自動的に操作を開始します。
start()
を使用して
start()
される別個のOperation操作は、 現在のスレッドで同期的に実行されることに注意してください。 現在のスレッドの外部で実行するには、
OperationQueue
または
DispatchQueue
を使用する必要があります。
アプリケーション内の任意の時点でのOperation操作の現在の状態は、ブールプロパティを使用して監視できます:
isReady
、
isExecuting
、
isFinished
、
isCancelled
KVO (
key-value observation
)メカニズムを使用し
key-value observation
操作自体はどのスレッドでも実行でき、最も必要な情報
main thread
または操作自体が実行されるスレッド以外の他のスレッドで。
Operation操作の機能を追加する場合は、
subclass
Operation
作成する必要があります。 最も単純な場合、この
subclass
は、
Operation
クラスの
main()
メソッドをオーバーライドする必要があります。
Operation
クラス自体が操作の状態の変化を自動的に制御しますが、以下に示すより複雑なケースでは、これを手動で行う必要があります。
操作には、操作の完了後に実行される
completionBlock
と、
OperationQueue
での操作の優先度に影響を与える「サービス品質」
qualityOfService
を提供できます。
ご覧のとおり、
Operation
クラスには
cancel()
メソッドがありますが、このメソッドを使用すると
isCancelled
プロパティが
true
設定されるだけで、操作を「削除」するという意味は
subclass
Operation
作成時にのみ決定できます。 たとえば、ネットワークからデータをダウンロードする場合、
cancel()
をネットワーク相互作用から操作を切断する
cancel()
定義できます。
基本的な概念。 OperationQueue
自分で操作を開始する代わりに、OperationQueueキューを使用して操作を管理します。 Operation Queue
OperationQueue
は、追加の機能を備えた
DispatchQueue
優先度の高い「ラッパー」と見なすことができます。実行された操作を破棄する機能、依存する操作を実行する機能などです。
OperationQueue
クラス
API
見てみましょう。
ここでは、
OperationQueue ()
操作キューの最も単純な初期化子と、
OperationQueue ()
および
main
キューの現在の操作キューを決定する
current
および
main
2つのクラスプロパティを参照します。これは、
GCD
DispatchQueue.main
と同様にユーザーインターフェイス(
UI
)を更新するために使用されます 非常に重要なプロパティ
maxConcurrentOperationCount
は、このキューの同時操作の数を設定し、 1に設定すると、操作の順次(
serial
)キューを確立します。
デフォルトでは、
maxConcurrentOperationCount
プロパティの値は
Default
に設定されます。これは、同時操作の最大可能数を意味します。
OperationQueue
操作(またはその
subclass
いずれか)を
Operation
subclass
、クロージャー、または操作の配列全体に直接追加して、
OperationQueue
配列全体が完了するまで現在のスレッドをブロックすることができます。
Operation Queue
OperationQueue
は、優先度
qualityOfService
、「準備完了」(
isReady
プロパティ
isReady
true
設定されて
true
)、および他の操作からの依存関係に従って、配置された操作を実行し
true
。 これらすべての特性が等しい場合、操作はキューに入れられた順序で「実行」のために送信されます。 操作が操作キューに配置されると、これらのキューのいずれにも再び配置できません。 操作が実行された場合、操作のキューで繰り返し実行することはできません。操作は1回限りです。したがって、
Operation
クラスの
subclasses
を作成し、必要に応じて使用して、この操作のインスタンスを取得します。
たとえば、アプリケーションがバックグラウンドモードで「離れる」場合、
cancellAllOperations ()
メソッドを使用して、キュー内のすべての操作に
cancel()
メッセージを送信できます。
waitUntilAllOperationsAreFinished()
メソッドを使用して、このキューに対するすべての操作が完了するまで現在のスレッドをブロックできます。 ただし、
main queue
これを実行しないでください。 すべての操作を完了した後にのみ何かを行う必要がある場合は、操作の
private
シリアルキュー(
serial queue
)を作成し、そこで操作の完了を待ちます。
OperationQueue操作キューは
DispatchGroup
ように動作します。
OperationQueue
でさまざまな
qualityOfService
して操作を追加できます。それらは優先度に従って起動されます。 また、操作キュー全体に対して
qualityOfService
をより高いレベルで設定することもできますが、この値は単一操作の
qualityOfService
の値によって再定義されます。
OperationQueue
のデフォルトの
qualityOfService
は
.background
です。
isSuspended
プロパティを
true
設定して、
OperationQueue
操作を停止することもでき
true
。 このキュー内の操作は続行されますが、
isSuspended
プロパティの値を
false
変更するまで、新しく追加された操作は実行のために送信されません。
isSuspended
プロパティのデフォルト値は
false
です。
Playground
Operation操作と
Operation
キューでいくつかの実験をしてみましょう。
実験1. OperationQueue
を作成してクロージャーを追加する
コードはGithubの
OperationQueue.playground
で表示できます。
空の
printerQueue
作成し
printerQueue
。
クロージャーの形式で操作を
printerQueue
。
printerQueue
を追加するとすぐに、操作は現在のスレッドに対して非同期に開始され、
ready
状態になります。
waitUntilAllOperationsAreFinished()
メソッドを使用してすべての操作の実行時間を推定します。このメソッドは、現在のスレッドに対して「同期的に」操作が完了するまで待機します。 アプリケーションの
main queue
では、これを行わない方が良いですが、
Playground
は
U
I
Playground
何も起こらず
Playground
余裕があります。 7つの操作すべての合計実行時間は2秒よりもわずかに長く、
sleep(2)
演算子の実行時間に対応するため、
printerQueue
は多くのスレッドで7つの操作すべてを同時に開始します。
printerQueue
キュー
printerQueue
変更し、
maxConcurrentOperationCount
プロパティを2に設定しましょう。
ペアで開始し、最後の7番目の操作が4番目の「ペア」の1つを開始するため、すべての操作を
printerQueue
に置くのに非常に短い時間がかかり、すべての操作を完了するのに8秒強かかります。
次に、
qualityOfService
を
.userInitiated
増やした別の
concatenationOperation
操作を追加し
.userInitiated
。
最初の機会に、優先度の高い操作が他の操作よりも早く実行されますが、すべての操作の合計実行時間は実質的に変わらないままで、8秒強です。
maxConcurrentOperationCount
プロパティを1に設定して、
maxConcurrentOperationCount
serial
maxConcurrentOperationCount
ましょう。
この場合、操作は順番に実行され、優先順位が機能せず、すべての操作の合計実行時間が16秒に増加することがわかります。
フィルター処理されたソース画像の配列を取得する場合、より複雑なケースを検討
前のセクションでおなじみのカスタム
FilterOperation
操作が使用されます。
フィルター操作用の
filterQueue
、フィルター処理されたイメージを配列に追加するスレッドセーフ用の
appendQueue
シーケンシャル(
serial
)キューを作成します。 実際には、多くのフィルタリング操作が
shared
リソース(
filteredImages
配列)に同時にアクセスして、要素を追加します。 覚えてる?
shared
リソースを変更するために
serial
DispatchQueue
キューを使用しましたか? これも同じですが、
Operation
に対して実行されます。 もちろん、シリアル(
serial
)
DispatchQueue
キューの方が効率的ですが、シリアル(
serial
)
OperatioQueue
キューの作成と使用を示します。
Swift
配列は
value type
あり、コピー時にコピー(
copy on write
)されるため、マルチスレッドの変更について心配する必要はありませんが、特に配列が型クラスのオブジェクトのプロパティである場合、これに問題があるかどうかについてフォーラムにメッセージがあります。 したがって、異なるスレッドで新しい要素を追加するときに
thread safe
配列を保持する方法を示します。
次に、各画像のフィルタリング操作を作成し、非同期実行のために
filterQueue
に追加します。 フィルター処理が完了したら、結果のイメージを
filteredImages
配列に追加し、
completionBlock
で
appendQueue
ます。ここで別の操作キュー
appendQueue
を使用します。
completionBlock
入力パラメーター
completionBlock
なく、何も返しません。
すべてのフィルタリング操作が完了するのを待って、フィルタリングされた画像配列を確認します。
すべての操作の実行時間は1.19秒です。これは、1つの操作(前のセクションを参照)の実行時間に匹敵します。
filterQueue
、
filterQueue
キューで操作のマルチスレッド実行が行われます。
Playground
は、フィルタリングされた画像の配列が表示されますが、フィルタリング効果を見るには非常に小さいです。 クイックビューボタンをクリックして、フィルタリング効果を確認できます。
非同期操作
コードはGithubの
AsyncOperations.playground
で表示できます。
これまでのところ、SYNCHRONOUSタスクの操作、つまり現在のスレッドを使用し、タスクが完了するまで戻らない関数を使用しました。 ASYNCHRONのタスク(関数)はまったく異なる方法で動作します:現在のスレッドですぐに制御を返し、別のスレッドでタスクを実行し、タスクが完了したことを知らせるため、他のスレッドで
completionHandler
が閉じられます。 典型的な例は
URLSession
です:
Operation操作で
URLSession
の機能を「ラップ」できますが、操作のステータスを手動で制御する必要があります。
SYNCHRONOUS関数のカスタム操作を作成するには、操作メソッド
main()
をオーバーライドするだけで済みました。 ASYNCHRON関数でまったく同じことを行う場合、
main()
ですぐに制御を現在のスレッドに戻し、別のスレッドで動作するために「脱退」し、
main()
メソッドの最後に移動し、
OperationQueue
すぐに
OperationQueue
キューから「スロー」します。 ASYNCHRON機能を完了することはありません。 これが
OperationQueue
背後にあるロジックです。
非同期操作には、まったく異なる作業ロジックがあります。
操作が「準備完了」(
isReady = true
)の場合、OperationQueue操作キューは
start()
メソッドを呼び出します。このメソッドでは、操作を「running」状態(
isExecuting = true
)に設定し、
main()
メソッドを呼び出します。このメソッドはASYNCHRON関数を呼び出します。 ASYNCHRON関数はANOTHERスレッドで何かを行いますが、
isExecuting
プロパティは、CURRENTスレッドで何も行わず、別のスレッドで実行されているタスクのみを表す場合でも
true
ままでなければなりません。 ASYNCHRON関数がASYNCHRON関数の
completionHandler
を示す
completionHandler
呼び出す場合、
completionHandler
プロパティ
isFinished
を
true
に、
isExecuting
プロパティを
false
設定する必要があり
false
。
したがって、ASYNCHRON操作の場合、
main()
メソッド以上のものをオーバーライドする必要があります。 次のメソッドとプロパティをオーバーライドする必要があります。
Operation
から継承され、ASYNCHRONOUS操作の実行に適した抽象カスタムクラス
AsyncOperaton
作成しましょう。 その抽象性は、非同期操作を実行する
main()
メソッドがないことです。 彼の図は次のとおりです。
OperationQueue
なしで自分でASYNCHRONOUS操作を使用する場合、
isAsynchronous
プロパティをオーバーライドして
true
を返す必要があります。 ASYNCHRON関数を実際に開始し、
isExecuting
プロパティを
true
保つには、
start()
メソッドをオーバーライドする必要があります。 また、操作の状態を決定するプロパティ
isReady
、
isExecuting
、
isFinished
を管理する方法を学ぶ必要があります。 これらのプロパティは、
OperationQueue
操作キューによって使用され、
OperationQueue
のステータスを監視し、依存する操作の実行を整理します。
操作間の依存関係を定義する場合、1つの操作が別の操作を開始する前に終了する必要があるため、
OperationQueue
操作キューは、操作がいつ終了するかを知ることが重要です。 SYNCHRONOUS操作の場合、SYNCHRONOUS機能が終了するとSYNCHRONOUS操作が終了するため、これは問題ではありません。 ただし、ASYNCHRON関数は現在のスレッドの外側で終了するため、ASYNCHRON関数の実際の終了について
OperationQueue
操作キューに通知する方法が必要です。
GCD
を思い出すと、ASYNCHRONOUS関数をグループに追加するときに、
enter()
および
leave()
メソッドを使用して、ASYNCHRONO関数の開始と終了を明確に示しました。 ただし、Operation操作の場合、操作には
isReady
、
isExecuting
、
isFinished
、
isCancelled
などの状態があるため、状況ははるかに複雑です
isReady
関数をOperation操作に追加する場合、これらの状態を手動で管理する必要があります。 この作業を容易にするために、
AsyncOperaton
という名前の
Operation
クラスの特別な抽象ユーザー
subclass
を作成しました。その主なタスクは、操作の状態の変更を制御することです。 独自のASYNCHRON関数の場合
subclass
クラスの
subclass
作成し、そこからASYNCHRON関数を呼び出す
main ()
のみを定義します。 そして、すでにこの新しい操作は
OperatonQueue
に追加され
OperatonQueue
。
ただし、問題は、たとえば、操作の状態に関連するすべてのプロパティ
isReady
、
isExecuting
、
isFinished
が
readonly
(
{get}
)であり、直接設定できないため、
isExecuting = true
記述できないこと
{get}
。
AsyncOperaton
操作のステータス
AsyncOperaton
変更されたことをシステムに通知しながら、
isExecuting
プロパティが
true
返すようにすることしかできません。
Operation
クラスは、 KVO (
key-value observation
)メカニズムと
willChangeValueForKe
および
didChangeValueForKey
を
isReady
、
isExecuting
、
isFinished
、
isExecuting
などの状態プロパティの変更について通知します。
したがって、資産管理業務の便宜のために、私たちは、新しいデータ型を作成します-適切な列挙
enum State
非同期操作独自の変種の状態を表すために:
ready
、
executing
、
finished
。
列挙
State
には、KVO通知のスイッチとして使用
fileprivate
する名前のプロパティも含まれます。プロパティは計算され、大文字の列挙要素の名前であるに接続された文字列で構成されます。次に、抽象クラスで操作の現在の状態を表す型変数を定義します。デフォルトでは、この値はequal です。変数を変更するたびに、KVO通知を切り替える必要があります。これを行うには、オブザーバーとプロパティを使用します。
keyPath
keyPath
"is"
rawValue
State
AsyncOperaton
var state
State
ready
state
willSet {}
didSet {}
state
state
たとえば、から
executing
に操作状態を切り替える前に、両方の操作の新しい状態()と現在の状態()についてKVO通知
finished
を送信する必要があります。切り替え条件が後に発生した、我々は送信KVOの通知をするために両方の条件:()と()。これは、システムが新しい値「ネイティブ」の状態変数の読み出し動作をするようになります、、。そのため、非同期操作我々は「ネイティブ」状態変数の操作を再定義する必要があり、、
willChangeValue
newValue
finished
state
executing
state
didChangeValue
keyPath
oldValue
executing
state
finished
isReady
isExecuting
isFinished
AsyncOperation
isReady
isExecuting
isFinished
state
正しい値を返す計算変数として新しいプロパティを使用します。これを
extension
クラス拡張で行い
AsyncOperation
ます:
ある時点で、操作のプロパティ
isReady
が等しくなり
true
、プロパティ
isReady
forを使用する必要があります。これは、他の操作
superclass
への依存性(
dependencies
)を「知覚」します。独自のロジックとのプロパティ
isReady
を組み合わせる
superclass
ことで、操作が本当に「準備完了」であることを確認できます。注変数があればそれ
state
で
.finished
、のプロパティ
superclass
isFinished
と同じ
true
、とプロパティ
isExecuting
-
false
。また、2つのメソッドを
isAsynchronous
返すことでプロパティを再定義します
true
:
start()
と
cancell()
。
メソッドで
start()
は、操作が破棄されているかどうか、つまりプロパティ
isCancelled
がequalであるかどうかを確認します
true
。その場合、変数に新しい値を設定する必要があります
state
-
.finished
。操作が破棄されない場合、を呼び出します
main()
。それは覚えている
main()
非同期関数であり、それはすぐに戻りますので、我々は手動操作の状況を設定する必要が
stat
同じです
.executing
。 ASYNCHRON関数が戻るとき、
completionHanller
操作の状態を設定しなければなりません
.finished
。 ASYNCHRON関数の場合、
start()
メソッドで
superclass
-の同様のメソッドの呼び出しを使用できないことを覚えておくことが非常に重要です。これは
super.start()
、関数の同期起動を意味し、
main()
正反対が必要だからです。
このメソッドで
cancell()
は、操作の状態も設定する必要があります
.finished
。
その結果、抽象クラスを取得しました
AsyncOperation
、独自の非同期操作に使用できます。これを行うには、次の手順を実行します。
例として、
sleep (1)
2つの数値の非同期低速(内部にある)加算の機能を使用します。
AsyncOperation
as を使用して
superclass
、再定義
main ()
し、操作状態
state
を
.finished
in に設定することを忘れないで
callback
ください:
callback
リターンの結果
result
、我々はプロパティ操作割り当て、
self.result
およびセット動作状態の
state
時に
.finished
ことを通知番号の非同期添加終了のすべての操作をして、もはやこの操作で作業する必要があります。
を使用して
SumOperation
、数値のペアの合計の配列を取得します。
数値のペアごと
SumOperation
に、
additionQueue
通常の方法で操作を作成し、操作のキューに配置します非同期操作の順序は、配列内の数値のペアの順序とはわずかに異なることがわかります。これは、非同期操作のマルチスレッド実行を示しています。
第二の例は、与えられた画像の非同期ローディングに関する
URL
介し
URLSession
:
非同期動作作成
ImageLoadOperation
前回のようにクラスのサブクラスです、、
AsyncOperation
。操作については
ImageLoadOperation
、前回のように、再定義
main ()
、および操作のステータスを設定することを忘れないでください
state
中
.finished
で
completion
:
操作を作成し
operationLoad
、画像を取得
operationLoad.outputImage
し、上に表示
view
:
コードは、上で閲覧することができます
AsyncOperations.playground
でのGithub。
依存関係(dependencies
)
コードはGithubの LoadAndFilter.playgroundで表示できます。
このセクションでは、1つの操作の結果を別の操作に転送し、最初の操作が終了したときにのみ2番目の操作を強制的に開始する方法を検討します。
図では、2つの操作が表示されます。1つ目はネットワークから画像を読み込み、2つ目は画像の上部と下部の「フォギング」を実行します-フィルタリング。
これらの操作は両方とも相互に非常にうまく機能します。イメージの「ダウンローダー」はそのデータを「フィルター」に直接転送します。
これらの両方の操作を含む1つの操作を作成できますが、これは非常に柔軟な設計ではありません。
アプリケーションのさまざまな場所でイメージを任意の順序で使用するために、イメージを操作する際に、操作のモジュール性をある程度確保することが望ましいです。
より柔軟なソリューションは、最初の操作から2番目の操作へのイメージの転送を伴う操作の「チェーン」の実装に関連付けられています。 「フィルター」は「ローダー」に依存していると判断でき、操作のキューは
OperationQueue
、「フィルター」が
ready
「ローダー」の終了後にのみ状態に設定されることを認識します。これは本当に操作キューのすばらしい「能力」です
OperationQueue
。 「依存関係」の非常に複雑なグラフを作成して、
OperationQueue
必要に応じて自動的に操作を開始することができます。「依存関係」の処理をサポート
API
するクラス
Operation
(
dependencies
)-非常にシンプルですが、操作中に素晴らしい力を発見します。
「依存関係」(
dependency
)を追加および削除したり
dependencies
、この操作に追加された「依存関係」のリストを取得したりできます。以下では、リスト
dependencies
を使用して、依存フィルタリング操作の入力画像を取得します。
依存関係を作成すると、デッドロック(deadlock)が発生する可能性が高くなり
ます。「依存関係」列に閉じたループが現れると、デッドロックが発生し、視覚的な分析によって特定する以外に、それらを排除する普遍的な方法はありません。
操作の「依存性」に関連する次の問題は、「依存性チェーン」に沿ってデータを転送する方法です。たとえば、上記の例では、「ローダー」が最初に機能し、次に「フィルター」が機能する場合、入力イメージは「ローダー」の出力イメージになります。
これをどのように達成しますか?この
ImagePass
場合、必要なデータを提供するプロトコルを作成します
UIImage?
:
ローダー「 -すでにおなじみのクラスの
ImageLoadOperation
彼は与えられた入り口にはネットワークのイメージロード操作。
URL
文字列などの画像
urlString
、および出力-イメージそのもの
outputImage
。
クラス
ImageLoadOperation
」確認「プロトコル
ImagePass
とプロトコルの特性を返す
image
出力は画像を読み込ま
outputImage
。
ターンでは、操作」フィルタリング「 -クラスを
FilterOperation
-入力画像が存在しない場合にはさ
_inputImage
依存性の確認」「
dependencies
とだけ人々に興味を持っている」確認「レポート
ImagePass
彼は依存として第1の動作を選択し、抽出物がそこ。
inputImage
:
」フィルタ「を入力で-入力画像
inputImage
、出力は関数を使用してフィルター処理された
filterImage (image:)
画像
outputImage
です。これは通常の同期操作なので、再定義するだけで済みます
main()
。
「フィルター」
inputImage
が「ロード」操作の出力イメージを入力イメージとして使用するように、これら2つの操作を連携させたいと思います。これを行うために、我々は、フィルタリングの動作を設定し
filter
た値で
nil
:
コードが上に配置されている
LoadAndFilter.playground
上のGithub。
上の操作の破壊 OperationQueue
コードが上に配置されている
Cancellation.playground
上のGithub。
キューの別の素晴らしい
OperationQueue
機会を見てみましょう-オペレーションを破壊する能力。
操作
Operation
をキューに配置した後は、操作を
OperationQueue
実行するための独自の計画があり、操作を完全に制御するため、操作を実行する方法はありません。ただし
Operation
、メソッドを使用して破棄することができます
cancel()
。
メソッドを呼び出す
cancel()
とすぐに操作が停止すると思うかもしれませんが、そうではありません。このメソッド
cancel()
は、
isCancelled
操作プロパティをに設定するだけ
true
です。操作がまだ開始されていない場合、デフォルトの方法
start()
操作の実行を許可せず、プロパティ
isFinished
をに設定します
true
。あなたは(オーバーライドする場合
override
)の方法を
start()
、あなたはあなたの能力保存しなければならない
start()
財産の取引があれば、操作の実行を防止するため
isCancelled
に設定されているが
true
。そして、抽象クラスを見ると、
AsyncOperation
まさにそれを行っていることがわかります。
さらに、
main()
操作メソッドで、特に低速またはリソース集約的な処理を実行する前に
isCancelled
、操作が既に破棄されているかどうかをプロパティでテストする必要があります。操作が破棄され、
true
プロパティの値が表示された場合、操作
isCancelled
を停止するために必要なアクションを実行する必要があります。操作の場合
Operation
たとえば、画像変換などのローカルで実行した場合、操作を停止できます。サーバーから画像をダウンロードするなど、ネットワークへのアクセスに関連する操作の場合、サーバーが結果を返すまでそのような操作を停止することはできません。
操作のさまざまな「ステップ」の間に「ロジック」を追加して、この操作を実行し続ける価値があるかどうか、または操作を
isFinished
等価状態に設定する価値があるかどうかを確認する必要があり
true
ます。操作を削除するように設計され
API
たクラス
Operation
は非常に単純で、2つの位置のみで構成されます。
メソッドを呼び出す
cancel()
-
isCancelled
操作プロパティをに設定し
true
ます。メソッドが呼び出されたときにことに注意することが重要であり
cancel()
、プロパティ
isExecuting
と
isFinished
もで変化
false
し、
true
それぞれ。
操作が
isCancelled = true
終了せずに()破壊されることは完全に正常です
isFinished = true
。プロパティ
isCancelled
は、停止する必要があることを操作に伝え、プロパティは、
isFinished
操作が既に停止していることをシステムに伝えます。
私たちの抽象非同期操作は、
AsyncOperation
メソッドをオーバーライドし
cancel()
、動作状態を設定する方法に
state
し
.finished
て、このような変更は、変更操作のプロパティを起こし
isFinished
と
isExecuting
:
キュー
OperationQueue
はすべての操作を破棄できます。
例としていくつかのカスタム操作を使用して、メソッド呼び出しに正しく応答する操作を取得する方法を見てみましょう
cancel()
。
コードが上に配置されている
Cancellation.playground
上のGithub。
操作に
ArraySumOperation
は、
inputArray
整数のペアで構成されるタプルの入力配列があり
outputArray
、出力でそれらの合計の配列を形成します。
数値の各ペアに対して、Cancellation.playground
slowAdd
のフォルダー
Source
にある「遅い」加算関数を使用し、出力配列に追加します。数値の入力配列を設定し、操作を形成し、操作キューに追加します
outputArray
sumOperation
queue
タイマーを起動し
sumOperation
ます
cancel()
。これにより、メソッド呼び出しに対する操作の反応を確認できる時間をさらに調整できます。さらに、操作には
completionBlock
、タイマーを停止し
Playground
outputArray
、操作をポイントして終了します
Playground
:
そのため、操作を完了するには
sumOperation
5秒以上かかります。今呼び出すことによって開始した後、2秒後に、この操作を破壊しよう
cancel ()
:
私たちは、予期しない結果を得ました-操作が
sumOperation
完全に無破壊操作が発生していない、実装されています。問題は何ですか?しかし、実際には、メソッド
cancel ()
はプロパティ
isCancelled
を
true
に設定するだけであり、操作の削除に必要なアクションは操作の開発者に委ねられます。プロパティがに
isCancelled
設定されているという事実に対応する必要があります
true
。出力配列に合計を追加する前に、操作が破棄されるかどうかを確認します。そして、それが破壊された場合、サイクルを中断します:
再開しましょう
Playground
:
2秒より少し遅れて停止し、2つの量を取得しました.3番目の量を取得しようとしたときに、操作の破壊に関するシグナルを取得し、量のさらなる受信を停止しました。この例は、コマンドに対するユーザー操作の応答を取得する方法を明確に示しています
cancel ()
。
タプル配列を介して出力配列ループを取得するために
AnotherArraySumOperation
別の関数が使用される点で異なる別の操作を見てみましょう
slowAddArray
:
前のケースとの違いは、タプル配列の要素を介したループが
main()
操作メソッドではなく、別の関数であり、私たちにとって難しいことです操作が破棄された場合、サイクルを中断します。しかし、非常に洗練されたものではありますが、このような可能性があります:
関数の入力には、整数
slowAddArray
の
input
ペアの配列に加えて、引数があります
progress
。これは
Optional
、配列の処理の深さを引数として取り、
Double(results.count) / Double(input.count
それを返す関数です
Bool
。これ
Bool
により、アレイの継続処理が決定されます。
方法で
main()
操作
AnotherArraySumOperation
(前の図)では、関数に
slowAddArray
配列を渡しました。
inputArray
引数は
progress
、「テール」クロージャーとして設計されており、
progress
印刷にプロパティを使用しました。プロパティ
progress
は
Double
ですので、100を乗算し、配列処理の完了率と操作の完了率を取得しました。次に、操作の破棄に対する応答を返します。これは、配列の処理を続行または中断する信号です。反応はプロパティの逆です
isCancelled
。
直前の操作を元に戻し
SumOperation
、新たな操作上
AnotherArraySumOperation
:
非継続事業をした後2秒、我々は同じ結果を得た-我々だけ扱うことができた2の配列に5- 操作が破棄される前、つまり40%。
操作中断遅延を4秒に設定します。5の配列の4つの要素、つまり80%は、操作が破棄さ
れる前に処理されました。個々の操作がプロパティに応答するため、破棄できることを確認することが非常に重要です。しかし、この方法を使用して、個々の取引の破壊に加えて、あなたはキュー操作にすべて開始の操作を削除することができます方法を使用して
isCancelled
cancel ()
OprationQueue
cancellAllOperations
。これは、単一の目的で機能する一連の操作がある場合に特に役立ちます。この目標は、複数の独立した操作を並行して実行すること、または次々に実行される依存操作のグラフにすることです。これらの両方のケースを検討します。
パターン1.独立した操作のグループと連携する
コードが上に配置されている
CancellationGroup.playground
上のGithub。前のセクションで示し
た操作と同じ結果が得られるようにタスクを設定
ArraySumOperation
しました。この操作はタプルの配列を受け取り、
(Int, Int)
スロー加算関数を使用し
slowAdd()
て、タプルを構成する数値の合計の配列を作成します。入力配列のコンポーネントのサイクルは内部に隠されてい
ArraySumOperation
ます。個別の非常に単純な型操作のグループを作成しましょう
SumOperation
。操作は、
SumOperation
入力ペアの2つの数値を追加し
inputPair
、低速加算機能を経由して
slowAdd()
、結果を返します
output
:
普通のクラスを作成
GroupAdd
管理
private
運営のキュー
queue
と一連の操作を
SumOperation
入力配列のすべてのペアの合計を計算し、出力配列に
outputArray
タプルを配置するために(
Int, Int, Int )
ソースデータと結果で構成され
ます:クラスのインスタンスを初期化するとき、数値
GroupAdd
の
input
ペアの入力配列が形成され、そこから型の演算が形成され
SumOperation
ます。
completionBlock
各演算で、結果が出力に追加されますアレイの
outputArray
単一上で実行され、
private
シリアルライン操作
appendOperation
を避けるために競合状態を。
クラスは、すべての一般的な操作がある
Operation
メソッドを:
start()
、
cancel ()
、
wait ()
。、私たちは、「複雑な操作」としてそれを考えることができるように
作成します。EQ emplyarクラス
GroupAdd
数値のペアの配列を入力に送ります:
startを実行し
groupAdd
、1秒待機して
cancel ()
から、メソッドを使用してすべての加算操作を操作キューから削除します。結果として、すべての動作(使用の完了後
wait()
に使用することができない
main queue
が、上にあることができる
Playground
)、我々は、出力配列を短縮:
結果は、上で閲覧することが可能
Playground
CancelletionGroup.playground
でのGithub。
パターン2.依存する操作のグループと連携する
コードが上に配置されている
CancellationFourImages.playground
上のGithub。
依存する操作のグループとして、既によく知られている相互接続された操作のグループを考えてみましょう
UI
。ネットワークからのイメージの読み込み、フィルタリング、変更です。クラスでシーケンスを並べるようにしてください
ImageProvider
にこれらの操作を管理します
OperationQueue
メソッドを使用して
start ()
、
wait ()
と
cancel ()
。
2つの抽象操作(つまり、メソッド実装を持たない操作
main()
)があります。 1つはすでに慣れ親しんでいるASYNCHRONOUS操作であり、もう1つは依存関係から入力イメージ
AsyncOperation
を
ImageTakeOperation
抽出する操作です。に基づいて
inputImage
dependecies
AsyncOperation
指定されたURLの「ネットワーク」から画像をダウンロードする操作を作成しましょう。
この操作
ImagePass
は、受信した画像outputImageを操作のチェーンに沿ってさらに送信するためのプロトコルを確認します。
抽象操作は、この操作の初期化中に指定されていない場合、依存関係から
ImageTakeOperation
入力イメージ
inputImage
を抽出し、シーケンシャル操作のチェーンでイメージを転送するために使用される
dependecies
既知のプロトコルを使用して出力イメージを「ピックアップ」できます
ImagePass
:
抽象クラス
ImageTakeOperation
を
superclass
操作として使用すると非常に便利です依存操作のチェーンに関与します。たとえば、フィルター操作の場合
Filter
:
または、「ヒップスター」スタイルで画像を「エージング」する操作の場合
PostProcessImageOperation
:
または、クロージャーを使用して環境に入力画像を「捨てる」操作の場合
ImageOutputOperation
:
クラスを見てみましょう
ImageProvider
。通常のクラスを作成して
ImageProvider
制御することを
private
キュー操作
operationQueue
、および一連の操作
dataLoad
、
filter
および
output
与えられたから画像をロードするためのURL、フィルタ、それを回路に渡し
completion
:
クラスは
ImageProvider
、すべての典型的な動作の持つ
Operation
メソッドを:
start()
、
cancel ()
、
wait ()
、私たちは、「複雑な操作として、それを考慮することができます「。
4つのクラスインスタンスを作成します
ImageProvider
。
イメージのロードを開始します:
操作の完了を待機しており、4つのイメージを取得します:
すべての操作の継続時間は10秒をわずかに超えています。
イメージのロードを開始し、6秒待って
cancel ()
から、メソッドを使用してすべての操作を削除します。その結果、1番目、3番目、4番目の3つの画像のみがダウンロードされます
。結果はGithubのプレイグラウンド
CancelletionFourImages.playground
で表示できます。
パターン3. TableViewControllerとCollectionViewControllerの操作
プロジェクトコードはGithubのフォルダー
OperationTableViewController
にあります。多くの場合、iOSアプリケーションのテーブルには画像が含まれており、その画像を受信するにはサーバーへのアクセスが必要です。また、前のセクションで説明した「フィルタリング」などの受信画像に対する追加アクションが含まれる場合があります。これにはかなりの時間がかかり、テーブルをスムーズにスクロールするには、画像を使用したすべての操作を外部で非同期に実行する必要があります。前のセクションで示したクラスのアプリケーションを見てみましょう。これは、ネットワークからイメージをロードし、それをフィルタリングおよび変更するための相互接続された操作のグループにすでに馴染みのあることを実行します。例として、1つだけで構成される非常に単純なアプリケーションを考えます
main queue
ImageProvider
UI
Image Table View Controller
、テーブルセルは、インターネットからダウンロードされたイメージとブートプロセスを示す活動の指標が含まれているどの:
こちらのクラスである
ImageTableViewController
スクリーンのサービスフラグメント
Image Table View Controller
:
クラスのモデルは
ImageTableViewController
8つの配列ですのURLを:
- エッフェル塔
- ヴェネツィア-他のものよりもはるかに長くロードおよびフィルタリング
- スコットランドの城
- 北極-02
- エッフェル塔
- 北極圏-16
- 北極-15
- 北極-12
ImageTableViewCell
画像がロードされるテーブルのセルのクラスは次のとおりです。
Public API
このクラスは、画像のURLアドレスを
imageURLString
含む文字列です。しかし、等しくないように設定すると、画像のロードは行われず、「回転ホイール」の形のインジケータのみが機能し始めます。ただし、既に何らかの方法でロードおよび処理されたイメージがあり、methodを呼び出している場合、ライトアニメーションを使用して画面上のこのセルに表示します。このクラスには、メソッドに値を割り当てると開始するアクティビティインジケータがあります。画像は、セルとテーブルの相互作用を担当するデリゲートメソッドにロードされます。
imageURLString
nil
image
updateImageViewWithImage
spinner
imageURLString
tableView( _ : cellForRowAt:)
UITableViewDelegate
UITableViewCell
UITableView
:
- —
tableView( _ : cellForRowAt:)
, - —
tableView( _ : willDisplay:forRowAt:)
, - —
tableView( _ : didEndDisplaying:forRowAt:)
,
このメソッドを可能な限り「促進」するために、クラス
ImageProvider
を使用した非同期イメージの読み込みの要求はメソッドの範囲外になります
tableView( _ : cellForRowAt:)
。
tableView( _ : willDisplay:forRowAt:)
デリゲートメソッドに配置され、セルが表示されるように準備します。別の
tableView( _ : didEndDisplaying:forRowAt:)
デリゲートメソッドは、セルが画面を離れるまでに完了しないイメージのダウンロード要求を破棄するために使用されます。これはかなり一般的なアプローチであり、動作するアプリケーションで使用できます
TableView
。これにより、テーブルのスクロールパフォーマンスが向上します。
しかし、最初に、
ImageProvider
このアプリケーションで使用されるクラスに戻ります。
ImageProvider
前のセクションで使用されたクラスバリアントとは対照的に
Playground
、簡略化された形式を使用します。つまり、クラスのインスタンスを初期化するとき
ImageProvider
、
isSuspended = true
操作キュー()を停止してから
ImageProvider
、メソッドを使用してクラスインスタンスを具体的に開始する必要はありません。
start()
初期化中に依存操作のチェーンをすぐに開始し、
waitUntilFinished
等しく設定します。
false
これは
Playground
アプリケーションではなく、同期メソッドを使用します
wait()
。
したがって、クラス
ImageProvider
にはイニシャライザがあり、その入力に、画像
imageURLString
のURLとクラスのこのインスタンスを作成した人に型の画像
completion
を
generic
返すClosure を含む文字列を指定する必要があります
UIImage?
ImageProvider
、計算されたプロパティを使用する代わりに
image
。入力回路
completion
には署名があります
(UIImage?) -> ()
。つまり、画像
UIImage?
を取得し、何も返しません。に戻るために使用できます
UITableViewController
。
さらに、クラスのインスタンスの破棄を許可する必要があります。これにより、
ImageProvider
すべての操作が完了する前にテーブルセルが画面を離れると、開始されたすべての操作が破棄されます。したがって、
cancel()
クラスにメソッドがあり
ImageProvider
ます。
そのため、このクラス
ImageProvider
は、サーバーからのイメージのロード、フィルタリング、およびメソッドへの配信に関する依存操作のグループの非同期実行を提供します
UITableViewDelegate
。必要に応じて、このクラスのインスタンスを削除できます。
に戻る
ImageTableViewController
。
メソッド
tableView( _ tableView:, cellForRowAt indexPath:)
に画像をロードする代わりに、別のデリゲートメソッド
tableView( _ tableView: , willDisplay cell:, forRowAt indexPath:)
でそれを行います- そして、メソッドの画像を削除します
tableView( _ tableView: , didEndDisplaying cell:, forRowAt indexPath:)
。これら2つのメソッドの
拡張機能
extension
を作成しましょう。メソッドから始めましょう
tableView( _ tableView: , willDisplay cell:, forRowAt indexPath:)
。
メソッドの
tableView( _ tableView:, cellForRowAt indexPath:)
ように、セル
cell
と
indexPath
。まず、標準の手順に従って、セルのタイプがであることを確認し
ImageTableViewCell
ます。次に、画像のURLとしての
imageProvider
文字列
imageURLs [ (indexPath as NSIndexPath).row ]
と、「テール」クロージャーとして設計されたクロージャーを作成します。クロージャーでは、このテーブルセルに表示する必要がある画像を取得します。これは、更新に使用する必要があります
image
UI
main queue
なぜなら
UI
、バックグラウンドキュー(
background queue
)で更新を行おうとすると、これは機能せず、なぜこの画像が表示されないのか不思議に思うからです。
main
クラスプロパティがあります
OperationQueue
が
main queue
、必要なセルを更新するメソッド
updateImageViewWithImage( image )
を呼び出す
main queue
だけ
UITableViewCell
です。
ここで、操作の削除の可能性について考える必要があります。これを行うには、作成されたものへのリンクを失う必要はありません
imageProvider
。そうしないと、後でそれを見つけて、それに関連付けられている操作を削除できません。
私たちは、クラスの先頭に移動
ImageTableViewController
し、Cという名前の新しいプロパティを追加
imageProviders
:
プロパティは、
imageProviders
型のオブジェクトの集合であり
imageProvider
、最初は空です。
ImageProvider.swiftファイルの下部を見てみましょう。あなたは、既存の拡張すでにそこにあるでしょう
extension
クラス
ImageProvider
プロトコルを確認し、
Hashable
セットに必要なの
Set
:
私たちは、計算プロパティを取得
hashValue
と平等のための比較演算子を
==
。これで、オブジェクトのインスタンスをインストールして比較できます
ImageProvider
。に戻る
ImageTableViewController
。これで、オブジェクトのインスタンスを追跡
ImageProvider
し、
imageProviders
現在アクティブなセットにそれらを追加できます。
このコードを見て、何が起こっているかを段階的に確認してみましょう。これはデリゲートメソッドです
tableView
、セルが画面に表示される直前に呼び出されます。この時点で、
ImageProvider
非同期的に読み込み、画像をフィルタリングし、結果の画像
image
をに返すものを作成し
completionHandler
ます。の
image
更新
UIImageView
に使用し
main queue
ます。その後、後で破壊できるよう
ImageProvider
に、多数で記憶し
imageProviders
ます。これは、次のデリゲートメソッド
tableView
で
tableView( _ tableView:, didEndDisplaying cell:, forRowAt indexPath:)
、セルが画面を離れた直後に呼び出される名前で行います。それは私たちがこのすべての操作を破壊する必要があることをここにある
ImageProvider
:
これを行うには、私たちが見つけ
ImageProvider
セル用
cell
及び方法を使用
cancel ()
するに
ImageProvider
、このプロバイダーのすべての操作を削除してから、プロバイダー自体をsetから削除します
imageProviders
。いつものように、最初に標準手順を実行して、セルのタイプがであることを確認してから、このセル
ImageTableViewCell
と同じ行を持つすべてのプロバイダーを見つけます
ImageURLString
。これらすべてのプロバイダーを調べて削除し、セットから削除します
imageProviders
。それが私たちがする必要があるすべてです。
アプリケーションを実行しましょう。
画像の読み込み中にアクティビティインジケータが動作し、スクロールが遅延なく非常に高速に動作するようになりました。画像はロードされるまで空で、アニメーション化されます。素晴らしい。
プロジェクトコードはGithubのフォルダー
OperationTableViewController
にあります。
Operation
OperationQueue
GCD
GCD
そして
Operations
、彼らがどのように異なるかが、表ショーでは、似たような機能の多くを持っています。
DispatchGroup
そして、
OperationQueue
それらはすべてのタスクの完全な完了に関連するイベントを処理できますが
waitUntilAllOperationsAreFinished
、キューのメソッドを開始するときは非常に注意する必要があり
OperationQueue
ます
main queue
。
依存関係(
dependecies
)に関して
GCD
は、タスクのチェーンを
private
順次(
serial
)に実装するだけ
DispatchQueue
です。しかし、これは最も強い側面
OperationQueue
です。依存関係(
dependecies
)
OperationQueue
は単なるチェーンよりも複雑になる可能性があり、異なるキューで操作を実行できます
OperationQueue
。
でバリアを使用できます
GCD
シーケンシャル(
serial
)キューが
DispatchQueue
適切でない場合の「ライター」と「リーダー」の問題を解決する。を使用してこの問題を解決する適切な方法は
OperationQueue
非常に紛らわしく、
flags
非常に特別な依存関係が必要です。
で
GCD
のみ削除でき
DispatchWorkItems
ます。
Operations
独自のメソッドを使用して操作を削除することも
cancel()
、すべての操作をすぐに削除することもできます
OperationQueue
。で短絡を削除でき
BlockOperation
ます。
との両方
GCD
で
Operations
、SYNCHRONOUS機能を非同期的に実行できます。そうすることで、この操作
Operation
は、実装を含め、この再利用可能な機能のすべてのデータをカプセル化するためのオブジェクト指向モデルを提供します
subclasses
Operation
。しかし、多くの場合、複雑な依存関係を持たない単純なタスクの場合は
GCD
、操作を作成するよりも「軽い」メソッドを使用する方が便利
Operation
です。さらに、
Dispatch
ブロックの
GCD
完了に要する時間は、ナノ秒からミリ秒までで、
Operation
通常は数ミリ秒から数分かかります。
おわりに
この記事では、操作に関する以下の質問を考慮し、
Operation
かつキュー操作
OperationQueue
:
1.操作が
Operation
問題と「ライフサイクル」とそのステータスを表示するプロパティを持つ1つのオブジェクト内のデータをカプセル化することができます。
2.
BlockOperation
オブジェクト指向の「ラッパー」
DispatchQueue.global()
であり、そのグループの制御を失うのではなく、クロージャーのグループの実行を追跡できます
DispatchQueue.global()
。すでにアプリケーションで使用していて、それらに干渉したくない場合は
BlockOperation
、代替として単純な操作に使用すると便利です。3.運用が開始されると、その主な可能性が明らかになります
GCD
Operations
DispatchQueue
Operation
OperationQueue
。操作を準備したら
Operation
、それを
OperationQueue
に渡します。これは、すべての操作の実行順序を制御します。これは、本質的に非常に単純なモデルであり、マルチスレッドプログラミングの複雑さを隠します。
OperationQueue
に似て
DispatchGroup
おり、異なるレベルの操作を組み合わせて
qualityOfService
、すべての操作が完了するまで待つことができます。ただし、この
sync
メソッドを呼び出すときは非常に注意する必要があります。
4.操作にASYNCHRON関数を含めるには
Operation
、その完了を正確に記録するために特別なことをする必要があります。KVO
AsyncOperation
を使用してASYNCHRON操作を手動で管理する必要があります。5.比類のない操作
Operation
それらを一連の操作に組み合わせて、複雑な依存関係グラフ(
dependencies
)を作成できるということです。これは、
Operation
1つ以上の他の操作
Operation
が完了するまで開始できない操作を非常に簡単に識別できることを意味します。この記事では、プロトコル
protocol
を使用し
Operation
て、依存関係グラフ(
dependencies
)の操作間でデータを転送する方法を示します。ただし
deadLock
、特に異なる操作の操作間に依存関係がある場合は、解決できないデッドロックを引き起こす可能性のあるサイクルを回避するために、依存関係グラフを調査する必要があります
OperationQueue
。
6.操作
Operation
を操作キューに転送したら
OperationQueue
、ターン
OperationQueue
自体が実行のための操作を開始するためのスケジュールを作成し、その実行を制御するため、この操作の制御を失います。ただし、メソッド
cancel ()
を使用して、操作が開始されないようにすることができます。この記事では
isCancelled
、操作の構築時にプロパティを考慮する
Operation
方法と、操作キューのすべての操作を完全に削除する方法を示します
OperationQueue
。
7.結論は、インターネットから取得した画像でテーブルをスクロールする必要があり、「フィルタリング」や「エージング」などの追加アクションが必要な場合の実際のシナリオを反映するアプリケーションの開発を示しています。これにはかなりの時間がかかり、テーブルをスムーズにスクロールするには、画像を使用したすべての操作を外部で非同期に実行する必要があります
main queue
。このアプリケーションでは、操作
Operation
、特に非同期操作
AsyncOperation
とその依存関係(
dependencies
)を操作するための幅広い技術を使用しました。これにより、大幅な改善を達成することができました
UI
。
一緒にこの記事で、以前のギブあなたの中にマルチスレッド処理の全体像
Swift 3
と
4
今存在しているiOSの上、。安定性()に加えて、マルチスレッドが
Swift
バージョンの
Swift 5
優先順位
ABI
として宣言されている場合、現在敷設されているマルチスレッド処理の将来の可能性の議論に全面的に参加できます
concurrency
。バージョン
Swift 5
完全に新しいマルチスレッドモデルの作業の開始のみが含まれ、その実装は将来のバージョンでも継続されます。Swift 5には、将来のマルチスレッドモデルの提案が既にあります。だから「モーターをつけろ!」そして行ってください。
Swiftの進化はここで見ることができます。
参照資料
→ WWDC2015。高度なNSOperations(セッション226)。
→ iOSのNSOperationsをお楽しみください
→ SwiftのNSOperationとNSOperationQueueのチュートリアル
→ GCDとOperationsのiOS同時実行性
→ IOSの 並行性
→ Swiftの同時実行性:1つの可能なアプローチ