Ios

GCDの基本



Gcd Basics



GCDのフルネームはGrandCentral Dispatchです。これは、大規模な中央スケジューリング、システムレベルのスレッド管理です。
GCDソースコード
最初に添付 GCD公式ドキュメント
公式紹介:
システムが管理するディスパッチキューに作業を送信することにより、マルチコアハードウェアでコードを同時に実行します。
翻訳:システム管理のディスパッチキューにタスクを追加することにより、マルチコアハードウェアでコードを同時に実行します。
システムレベルで動作するGCDは、実行中のすべてのアプリケーションのニーズに適切に対応し、それらを利用可能なシステムリソースにバランスよく一致させることができます。
翻訳:GCDはシステムレベルで実行され、実行中のすべてのアプリケーションのニーズをより適切に満たすことができ、バランスの取れた方法で利用可能なシステムリソースにそれらを一致させます。

一文:GCDはシステムレベルのスレッド管理(タスクのパフォーマンスが非常に高い)であり、CPUコア(マルチコア)を自動的に使用し、GCDはスレッドのサイクルを自動的に管理します-破棄するスケジュールを作成します。

非常に強力で、木があります。アプリケーションレベルではできないことを実行します。 GCDはC言語であり、多くの実用的なAPIをカプセル化します。一緒に学びましょう。



まず、キューとタスク

  • キュー :FIFOの原則に従う一般的に使用されるデータ構造です
  • シリアルキュー :順次実行(次のタスクをキューから削除する前にタスクが完了します)
  • 並行キュー :同時に多くのタスクを実行します(実行するスレッドがあれば、同時に多くのタスクを実行できます)
  • 同期タスク :syncの優先度は高く、スレッドには実行順序があり、新しいスレッドは開かれません。
  • 非同期タスク :asyncの優先度は低く、スレッドの実行に順序はなく、CPUはアイドル状態ではありません。新しいスレッドはメインキューで開かれず、他のキューは新しいスレッドを開きます。
  • メインキュー :dispatch_get_main_queue()システムは、グローバルに利用可能なシリアルキューを提供し、非同期タスクを追加でき、同期タスクを追加できません
  • グローバルキュー :システムによって提供されるグローバルに使用可能な同時キューは、優先順位に従って4つの同時キューに分割されます。

重要:同期タスクをメインキューに追加すると、デッドロックが発生します

1、グローバル並行キュー

dispatch_get_global_queue(, )

識別する
このキューに対して実行されるタスクに指定されたサービス品質。サービス品質は、キュー実行タスクの優先度を決定するのに役立ちます。サービス品質は高い優先度です。
QoS:サービス品質
フラグ :予約済みパラメータ、0

ディスパッチキューの優先順位 GCD QoSクラス(sys / qos.hで定義)
DISPATCH_QUEUE_PRIORITY_HIGH QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_DEFAULT QOS_CLASS_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND QOS_CLASS_BACKGROUND
GCD QoSクラス(sys / qos.hで定義) 対応するFoundationQoSクラス
QOS_CLASS_USER_INTERACTIVE NSQualityOfServiceUserInteractive
QOS_CLASS_USER_INITIATED NSQualityOfServiceUserInitiated
QOS_CLASS_DEFAULT NSQualityOfServiceDefault
QOS_CLASS_UTILITY NSQualityOfServiceUtility
QOS_CLASS_BACKGROUND NSQualityOfServiceBackground
グローバルキュー 対応するQoSクラス
メインスレッド ユーザーインタラクティブ
DISPATCH_QUEUE_PRIORITY_HIGH ユーザー主導
DISPATCH_QUEUE_PRIORITY_DEFAULT デフォルト
DISPATCH_QUEUE_PRIORITY_LOW ユーティリティ
DISPATCH_QUEUE_PRIORITY_BACKGROUND バックグラウンド
  • QOS_CLASS_USER_INTERACTIVE :ユーザーインタラクティブメインスレッドでの作業、ユーザーインターフェイスの更新、アニメーションの実行など、ユーザーとの対話。作業がすぐに行われない場合、ユーザーインターフェイスがフリーズする可能性があります。応答性とパフォーマンスに焦点を当てます。メインスレッドはこのレベルで機能します。
  • QOS_CLASS_USER_INITIATED :User-initiatedは、ユーザーとのさらなる対話のために即時の結果を必要とするユーザートリガータスクを実行するために使用されます。たとえば、保存したドキュメントを開いたり、メーリングリストを選択したりした場合は、メールを読み込む必要があります。
  • QOS_CLASS_UTILITY :作業が完了するまでに時間がかかる場合があり、すぐに結果を出す必要はありません。実用的なタスクには通常、ユーザーに表示される進行状況バーがあります。応答性、パフォーマンス、エネルギー効率のバランスをとることに焦点を当てます。たとえば、定期的なコンテンツの更新や、メディアのインポートなどのファイルの一括操作。
  • QOS_CLASS_BACKGROUND :バックグラウンドで実行されている作業は、エネルギー効率に重点を置いて、ユーザーには見えません。インデックス作成、同期、バックアップなど。
  • QOS_CLASS_DEFAULT :このQoSの優先順位は、ユーザー対話型とユーザー主導型の間です。このQoSは、開発者が作業を分類するために使用することを意図したものではありません。 QoS情報を割り当てない作業はデフォルト値と見なされ、GCDグローバルキューはこのレベルで実行されます。

[チップ] NSOperationのデフォルトのQoSはNSQualityOfServiceBackgroundです



【優先逆転】
タスク間の関係が複雑な場合、優先順位は可能な限り単純であることに注意することが重要です。そうしないと、優先順位が逆転し、非常に有害です。
優先順位の逆転により、タスクは直接混乱し、論理的に混乱します。さらに、タスクがスケジュールされているとき、時間は不確実です。リアルタイムシステムのリアルタイムパフォーマンスが破壊され、深刻な場合にシステムクラッシュが発生する可能性があります。

では、優先順位の逆転とは何ですか?

優先度の逆転は、優先度の高い作業が優先度の低い作業に依存している場合、または優先度の低い作業の結果になる場合に発生します。その結果、ブロッキング、ローテーション、およびポーリングが発生する可能性があります。
同期操作の場合、システムは、反転中に優先度の低い作業のQoSを上げることにより、優先度の反転を自動的に解決します。これは、次の状況で発生します。
シリアルキューでdispatch_sync()とdispatch_wait()が呼び出されたとき。
pthread_mutex_lock()が呼び出されると、ミューテックスはQoSの低いスレッドによって制御されます。この場合、ロックを保持しているスレッドは呼び出し元のQoSに引き上げられます。ただし、このQoSアップグレードは、複数のロック間では発生しません。
非同期操作の場合、システムはシリアルキューで発生する優先順位の逆転を解決しようとします。

優先ソリューション?

開発者は、優先順位の逆転が最初から発生しないようにする必要があります。これにより、システムが解決を試みる必要がなくなります。



2、カスタムキュー

/ / Create a non-main thread experimental environment dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog (@'Add task before--current thread--%@',[NSThread currentThread]) / / Serial queue synchronization execution / / Create a serial queue The first parameter: give the queue a name Second parameter: queue properties, serial or parallel dispatch_queue_t serialQueue = dispatch_queue_create('serialQueue', DISPATCH_QUEUE_SERIAL) / / Add asynchronous tasks for (NSInteger i = 0 i <5 i++) { dispatch_async(serialQueue, ^{ NSLog(@'serial queue asynchronous task %ld--current thread--%@',i,[NSThread currentThread]) }) } / / Add synchronization task for (NSInteger i = 0 i < 5 i++) { dispatch_sync(serialQueue, ^{ NSLog (@'serial queue synchronization task %ld--current thread--%@',i,[NSThread currentThread]) }) } }) Console output 2017-10-24 10:11:19.243669+0800 GCD[21493:746313] Before adding a task--current thread--{number = 3, name = (null)} 2017-10-24 10:11:19.243890+0800 GCD[21493:746312] Serial Queue Asynchronous Task 0--Current Thread--{number = 4, name = (null)} 2017-10-24 10:11:19.244002+0800 GCD[21493:746312] Serial Queue Asynchronous Task 1 - Current Thread--{number = 4, name = (null)} 2017-10-24 10:11:19.244152+0800 GCD[21493:746312] Serial Queue Asynchronous Task 2 - Current Thread--{number = 4, name = (null)} 2017-10-24 10:11:19.244313+0800 GCD[21493:746312] Serial Queue Asynchronous Task 3 - Current Thread--{number = 4, name = (null)} 2017-10-24 10:11:19.244572+0800 GCD[21493:746312] Serial Queue Asynchronous Task 4--Current Thread--{number = 4, name = (null)} 2017-10-24 10:11:19.246669+0800 GCD[21493:746313] Serial Queue Synchronization Task 0--current thread--{number = 3, name = (null)} 2017-10-24 10:11:19.247604+0800 GCD[21493:746313] Serial Queue Synchronization Task 1--current thread--{number = 3, name = (null)} 2017-10-24 10:11:19.248015+0800 GCD[21493:746313] Serial Queue Synchronization Task 2 - Current Thread--{number = 3, name = (null)} 2017-10-24 10:11:19.248445+0800 GCD[21493:746313] Serial Queue Synchronization Task 3 - Current Thread--{number = 3, name = (null)} 2017-10-24 10:11:19.248746+0800 GCD[21493:746313] Serial Queue Synchronization Task 4--current thread--{number = 3, name = (null)}

シリアルキュー非同期タスク:スレッドを開き、タスクを1つずつ順番に実行します

シリアルキュー同期タスク:新しいスレッドを開かず、現在のスレッドでタスクを1つずつ実行します

/ / Create a non-main thread experimental environment dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ / / Create a parallel queue dispatch_queue_t currentQueue = dispatch_queue_create('currentQueue', DISPATCH_QUEUE_CONCURRENT) NSLog (@'Add task before--current thread--%@',[NSThread currentThread]) / / Add asynchronous tasks for (NSInteger i = 0 i <5 i++) { dispatch_async(currentQueue, ^{ NSLog (@' parallel queue asynchronous task %ld--current thread--%@',i,[NSThread currentThread]) }) } / / Add synchronization task for (NSInteger i = 0 i < 5 i++) { dispatch_sync(currentQueue, ^{ NSLog (@'parallel queue synchronization task %ld--current thread--%@',i,[NSThread currentThread]) }) } }) 2017-10-24 10:36:53.944242+0800 GCD[21646:774599] Before adding a task--current thread--{number = 3, name = (null)} 2017-10-24 10:36:53.944488+0800 GCD[21646:774603] Parallel Queue Asynchronous Task 1--Current Thread--{number = 5, name = (null)} 2017-10-24 10:36:53.944493+0800 GCD[21646:774599] Parallel Queue Synchronization Task 0--current thread--{number = 3, name = (null)} 2017-10-24 10:36:53.944489+0800 GCD[21646:774597] Parallel Queue Asynchronous Task 2 - Current Thread--{number = 4, name = (null)} 2017-10-24 10:36:53.944628+0800 GCD[21646:774603] Parallel Queue Asynchronous Task 3 - Current Thread--{number = 5, name = (null)} 2017-10-24 10:36:53.944527+0800 GCD[21646:774596] Parallel Queue Asynchronous Task 0--current thread--{number = 6, name = (null)} 2017-10-24 10:36:53.944635+0800 GCD[21646:774599] Parallel Queue Synchronization Task 1--current thread--{number = 3, name = (null)} 2017-10-24 10:36:53.944703+0800 GCD[21646:774598] Parallel Queue Asynchronous Task 4--current thread--{number = 7, name = (null)} 2017-10-24 10:36:53.945277+0800 GCD[21646:774599] Parallel Queue Synchronization Task 2 - Current Thread--{number = 3, name = (null)} 2017-10-24 10:36:53.945801+0800 GCD[21646:774599] Parallel Queue Synchronization Task 3 - Current Thread--{number = 3, name = (null)} 2017-10-24 10:36:53.946104+0800 GCD[21646:774599] Parallel Queue Synchronization Task 4--current thread--{number = 3, name = (null)}

並列キュー非同期タスク:複数のスレッドを開き、同時に実行し、順序なしで実行します

並列キュー同期タスク:スレッドを開かず、現在のスレッドで1つずつ順番に実行します

/* assign a priority to the custom queue *attr: queue attribute, string or union *qos_class:QoS *relative_priority: Must be a number less than 0 greater than QOS_MIN_RELATIVE_PRIORITY(-15) */ dispatch_queue_attr_t att = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1) dispatch_queue_t serialQueue2 = dispatch_queue_create('seiialqueue2', att) dispatch_async(serialQueue2, ^{ / / Add the task you need to perform })

第二に、実用的なAPI

1、dispatch_once
アプリケーションの存続期間内に、ブロック内で1回だけ実行します。
【アプリケーションシナリオ】 :シングルトンを作成する

#import 'Test8.h' @implementation Test8 + (Test8 *)shareInstance{ static Test8 * test8 = nil static dispatch_once_t onceToken dispatch_once(&onceToken, ^{ test8 = [[Test8 alloc] init] }) return test8 } @end

2、dispatch_time
デフォルトのクロックを基準にしてdispatch_time_tを作成するため、または既存のdispatch_function_tを変更するために使用されます。
デフォルトのクロック:mach_absolute_timeに基づいています。
【アプリケーションシナリオ】 他のAPIを使用して、タイミング操作と遅延操作を実装します。

dispatch_time_t time = dispatch_time(, )

最初のパラメータ:いつ

DISPATCH_TIME_NOW//current time DISPATCH_TIME_FOREVER//a very long time Usually used to control whether the timer starts

2番目のパラメーター:デルタ
時間パラメータに基づいて追加されたナノ秒数。

1 second (s) = 1000000000 nanoseconds (ns) 1 millisecond (ms) = 1000000 nanoseconds (ns) 1 second (s) = 1,000,000 microseconds (μs) 1 microsecond (μs) = 1000 nanoseconds (ns) #define NSEC_PER_SEC 1000000000ull //How many nanoseconds per second #define NSEC_PER_MSEC 1000000ull //How many nanoseconds per millisecond #define USEC_PER_SEC 1000000ull //How many subtle per second? #define NSEC_PER_USEC 1000ull //How many nanoseconds per microsecond

したがって、1秒は通常そうなる可能性があります

1*NSEC_PER_SEC 1000*NSEC_PER_MSEC NSEC_PER_USEC*NSEC_PER_MSEC NSEC_PER_USEC* USEC_PER_SEC

3、dispatch_after遅延操作
この関数は、指定された時間待機してから、ブロックを指定されたキューに非同期的に追加します。すぐに実行する代わりに。
【アプリケーションシナリオ】 ページが読み込まれた後のポップアップ広告

dispatch_after(, , ) //Create a time dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC) dispatch_after(time, dispatch_get_main_queue(), ^{ / / Wait for time seconds, the block will be submitted asynchronously to the main queue NSLog(@'2-1') })

whenパラメーターとしてDISPATCH_TIME_NOWを渡すことはサポートされていますが、dispatch_asyncを呼び出すほど良くはありません。 DISPATCH_TIME_FOREVERの受け渡しは未定義です。

dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^{ NSLog(@'2-1') })

4、dispatch_barrier_async
バリアによって送信されたブロックは、バリアが完了する前に送信されたタスクを待機します。バリアタスクの実行中は、バリアタスクの実装のみが実行されます。
【注意】 Dispatch_barrier_asyncは、それ自体が作成する同時キュー、グローバル同時キューおよびシリアルキューでのみ機能し、dispatch_syncと同じ効果があります。
【アプリケーションシナリオ】 リソースの競合を回避します。例:同じファイルの読み取りと書き込み。dispatch_barrier_asyncを介して書き込み操作を送信できます。

dispatch_queue_t currentQueue = dispatch_queue_create('com.starming.gcddemo.secondqueue', DISPATCH_QUEUE_CONCURRENT) for (NSInteger i = 0 i <5 i++) { dispatch_async(currentQueue, ^{ NSLog (@'pre-task %ld', i) }) } dispatch_barrier_async(currentQueue, ^{ NSLog(@'barrier task') }) for (NSInteger i = 0 i < 5 i++) { dispatch_async(currentQueue, ^{ NSLog (@'post task %ld', i) }) }

コンソール出力

2017-10-26 13:34:35.632559+0800 GCD[30827:2384918] Pre-task 1 2017-10-26 13:34:35.632559+0800 GCD[30827:2384912] Pre-task 0 2017-10-26 13:34:35.632560+0800 GCD[30827:2384915] Pre-Task 3 2017-10-26 13:34:35.632771+0800 GCD[30827:2384918] Pre-Task 4 2017-10-26 13:34:35.632604+0800 GCD[30827:2384913] Pre-Task 2 2017-10-26 13:34:35.634816+0800 GCD[30827:2384913] barrier task 2017-10-26 13:34:35.635281+0800 GCD[30827:2384918] Post task 0 2017-10-26 13:34:35.635285+0800 GCD[30827:2384912] Post Task 3 2017-10-26 13:34:35.635287+0800 GCD[30827:2384915] Post task 2 2017-10-26 13:34:35.635302+0800 GCD[30827:2384913] Post task 1 2017-10-26 13:34:35.635637+0800 GCD[30827:2384914] Post task 4

5、dispatch_group_async
ディスパッチグループは、異なるキューで実行されている場合でも、複数の非同期タスクをリッスンできます。完了すると通知が届きます。
dispatch_group_notify:スレッドをブロックせずにクロージャを非同期で実行します。グループリスニングタスクが完了すると、通知ブロックが呼び出されます。
dispatch_group_wait:ブロックされ、以前に監視されたタスクが完了するのを待ち、0を正常に返します。
いずれにせよ、グループがクリアされた後、タスクを追加し続けることができます。
【アプリケーションシナリオ】 弊社のAPPはハイブリッドモードです。アプリケーションを起動すると、リソースリストに従って変更されたリソースファイルがダウンロードされます。ダウンロードが完了した後、サーバーはダウンロード結果をフィードバックする必要があります。
2つの方法の違いは次のとおりです。

/ / Create three concurrent queues dispatch_queue_t current1 = dispatch_queue_create('current1', DISPATCH_QUEUE_CONCURRENT) dispatch_queue_t current2 = dispatch_queue_create('current2', DISPATCH_QUEUE_CONCURRENT) dispatch_queue_t current3 = dispatch_queue_create('current3', DISPATCH_QUEUE_CONCURRENT) dispatch_group_t group = dispatch_group_create() //Download image dispatch_group_async(group, current1, ^{ NSLog (@'group task download picture 1---currentThread%@', [NSThread currentThread]) }) //Download image dispatch_group_async(group, current2, ^{ NSLog (@'group task download picture 2---currentThread%@',[NSThread currentThread]) }) //Download image dispatch_group_async(group, current3, ^{ NSLog (@'group task download image 3---currentThread%@',[NSThread currentThread]) }) dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog (@' image download completed, main thread display') }) // dispatch_group_wait(group, DISPATCH_TIME_FOREVER) // NSLog (@'image download completed, main thread display') / / Listen to a task again /**dispatch_group_enter * Allows applications to explicitly add and remove tasks from a group in a way other than using the dispatch_group_async function. * This allows the task reference count to be managed correctly. Calling this function must be balanced with calling dispatch_group_leave. *You can use this feature to associate a block with multiple groups at the same time. */ dispatch_async(current2, ^{ / / indicates that the group has been entered, is another way to join the group different from dispatch_group_async dispatch_group_enter(group) NSLog (@'group task download picture 4---currentThread%@',[NSThread currentThread]) dispatch_group_leave(group) })

コンソール

2017-10-26 17:21:12.387513+0800 GCD[32025:2599998] group task download picture 1---currentThread{number = 3, name = (null)} 2017-10-26 17:21:12.387619+0800 GCD[32025:2599996] group task download picture 4---currentThread{number = 6, name = (null)} 2017-10-26 17:21:12.387624+0800 GCD[32025:2600001] group task download picture 3---currentThread{number = 5, name = (null)} 2017-10-26 17:21:12.387649+0800 GCD[32025:2599995] group task download picture 2---currentThread{number = 4, name = (null)} 2017-10-26 17:21:12.394213+0800 GCD[32025:2599907] The picture is downloaded, the main thread shows

コメントアウト

dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog (@' image download completed, main thread display') })

dispatch_group_waitを使用して、実行が終了するのを待ちます

dispatch_group_wait(group, DISPATCH_TIME_FOREVER) NSLog (@' image download completed, main thread display')

コンソールの結果

2017-10-26 17:25:43.719035+0800 GCD[32080:2606880] group task download picture 3---currentThread{number = 5, name = (null)} 2017-10-26 17:25:43.719053+0800 GCD[32080:2606878] group task download picture 1---currentThread{number = 3, name = (null)} 2017-10-26 17:25:43.719035+0800 GCD[32080:2606879] group task download picture 2---currentThread{number = 4, name = (null)} 2017-10-26 17:25:43.719265+0800 GCD[32080:2606733] The picture is downloaded, the main thread shows 2017-10-26 17:25:43.719820+0800 GCD[32080:2606879] group task download picture 4---currentThread{number = 4, name = (null)}

6、dispatch_apply
forループと同様に、複数の呼び出しのためにブロックをディスパッチキューに送信しますが、dispatch_applyはすべてのブロックが完了するまで待機します。
公式に述べた:この関数を並行キューで使用すると、効率的な並列ループになる可能性があります。
【アプリケーションシナリオ】 ハイブリッドアプリケーションがリソースリストに従ってリソースをダウンロードするために使用し、dispatch_group_enterとともに使用して、リソースのダウンロードが成功した後にダウンロード結果をアップロードします。

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t))

反復: 実行する反復の数。
キュー: スケジューリングキュー
ブロック: タスクを実行するためのブロック
size_t: typedef typeof(sizeof(int))size_t

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) NSArray * arr = @[@'Begar Lakeside', @'10:30 subway', @'When you are old', @' ', @' ', @' '] // The second parameter, queue, can be a parallel queue. It is recommended that DISPATCH_APPLY_AUTO will automatically select a queue suitable for the calling thread. dispatch_apply(arr.count, queue, ^(size_t i) { NSLog(@'dispatch_apply %zu %@ currentThread %@',i,arr[i],[NSThread currentThread]) })

コンソール

2017-10-27 10:30:32.752105+0800 GCD[34170:3051371] dispatch_apply 0 Baikal currentThread {number = 3, name = (null)} 2017-10-27 10:30:32.752101+0800 GCD[34170:3051273] dispatch_apply 3 Spring breeze is not as good as you currentThread {number = 1, name = main} 2017-10-27 10:30:32.752105+0800 GCD[34170:3051372] dispatch_apply 2 When you are old currentThread {number = 5, name = (null)} 2017-10-27 10:30:32.752107+0800 GCD[34170:3051370] dispatch_apply 1 10:30 subway currentThread {number = 4, name = (null)} 2017-10-27 10:30:32.752339+0800 GCD[34170:3051273] dispatch_apply 5 Father's prose poem currentThread {number = 1, name = main} 2017-10-27 10:30:32.752338+0800 GCD[34170:3051371] dispatch_apply 4 wind blowing wheat currentThread {number = 3, name = (null)}

7、dispatch_source_create
システムの基礎となるオブジェクトをリッスンするための新しいスケジューリングリソースを作成し、イベントに応答してブロックをディスパッチキューに自動的に送信します。

dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)

タイプ: ディスパッチソースのタイプ、 利用可能な値を入力します
扱う: タイプに応じて異なる値
マスク: タイプに応じて異なる値

タイプ 使用する ハンドルの値 マスク値
DISPATCH_SOURCE_TYPE_DATA_ADD データの追加 0 0
DISPATCH_SOURCE_TYPE_DATA_OR データをビットで結合するOR 0 0
DISPATCH_SOURCE_TYPE_MACH_RECV Machポートはメッセージを受け取ります mach_port_t 0
DISPATCH_SOURCE_TYPE_MACH_SEND Machポートがメッセージを送信します mach_port_t ディスパッチソースマッハ送信イベントフラグ
DISPATCH_SOURCE_TYPE_PROC イベントプロセス pid_t ディスパッチソースプロセスイベントフラグ
DISPATCH_SOURCE_TYPE_READ ファイル読み取り可能 ファイル記述子(int) 0
DISPATCH_SOURCE_TYPE_SIGNAL 信号処理 シグナル(int) 0
DISPATCH_SOURCE_TYPE_TIMER タイマー 0 0
DISPATCH_SOURCE_TYPE_VNODE ファイルシステムの変更 ファイル記述子(int) ディスパッチソースVnodeイベントフラグ
DISPATCH_SOURCE_TYPE_WRITE 書き込み可能なファイル ファイル記述子(int) 0
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE システムメモリの状態 0 FOO
void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway)

同じスケジューリングタイマーソースオブジェクトでdispatch_source_set_timerメソッドを複数回呼び出して、必要に応じてタイマーソース間隔をリセットできます。
起動時間パラメーターは、タイマーに使用されるクロックも決定します。開始時刻がDISPATCH_TIME_NOWであるか、dispatch_timeを使用して作成されている場合、タイマーはmach_absolute_timeに基づいています。それ以外の場合、タイマーの開始時刻がdispatch_walltimeを使用して作成されている場合、タイマーはgettimeofday(3)に基づいています。
タイマーソースがキャンセルされている場合、この機能の呼び出しは機能しません。
開始: タイミング開始時間
間隔: ナノ秒レベルの間隔、DISPATCH_TIME_FOREVERは、1回限りのタイマーを表します
余裕: ナノ秒レベルでの許容遅延。これは、タイマーをトリガーするために必要な精度です。
【説明の余裕】 5秒ごとにトリガーが必要で、精度が高い場合は、0を送信できます。 15分ごとにメールをクエリするなどの定期的なタスクの場合、時間要件はそれほど正確である必要はありません。60秒を経過して、60秒のエラーが許容できることをシステムに通知できます。このようにして、スレッドを頻繁に切り替えることなく、システムを他のタスクと組み合わせて実行できるため、システムの消費電力が削減され、システムのパフォーマンスが向上します。 (スレッドの切り替えにはシステムのオーバーヘッドがあります)
【注意】 すべてのタイマーで、余裕がゼロに指定されている場合でも、ある程度の遅延があります。

void dispatch_source_cancel(dispatch_source_t source)

イベントを処理するハンドラーの継続をブロックしますが、すでに進行中のブロックタスクを中断しません。イベントが完了すると、キャンセルハンドラーがターゲットキューに送信されます。キャンセルハンドラは、システムが基盤となるすべてのシステムオブジェクト(ファイル記述子またはマッハポート)への参照を解放した後、dispatch_sourceのターゲットキューにコミットされます。したがって、キャンセルハンドラは、このようなシステムオブジェクトを閉じたり解放したりするのに便利な場所です。ただし、キャンセルハンドラーを呼び出す前に、ファイル記述子を閉じるか、追加されたマッハポートを再利用するタスクは無効です。

GCDタイマー:

#import 'TestViewController.h' @interface TestViewController () @property (nonatomic, strong) dispatch_source_t timer @property (nonatomic, assign) BOOL isSuspend / / Is it suspended? @end @implementation TestViewController - (void)viewDidLoad { [super viewDidLoad] // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor whiteColor] [self creatTimer] typeof(self) weakSelf = self / / After the system releases the reference of all the underlying system objects, the block will execute dispatch_source_set_cancel_handler(self.timer, ^{ NSLog (@'timer destroyed %@', weakSelf.timer) }) } / / Create a timer⏲ - (void)creatTimer{ if (!self.timer) { self.isSuspend = NO //timer dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC, 0 * NSEC_PER_SEC) / / Event handling handler dispatch_source_set_event_handler(timer, ^{ NSLog (@'timed task') }) //start up /** It is recommended to use dispatch_activate for the first time, because dispatch objects like queues and sources may *inactive state is created */ dispatch_activate(timer) / / Create a property, the timer needs a strong reference, after the local variable is destroyed, the timing operation will not work. self.timer = timer } } //Start ▶️ - (IBAction)resumeTimer{ if (self.timer&&self.isSuspend) { self.isSuspend = NO / / If the suspension count counter is 0, and is non-inactive state, calling this method will trigger the assertion to terminate the process dispatch_resume(self.timer) NSLog(@'call dispatch_resume evokes a thread') } } //Pause ⏸ - (IBAction)suspendTimer{ if (self.timer) { self.isSuspend = YES //The suspended object will not continue to call any associated block, but will not interrupt the block being executed. dispatch_suspend(self.timer) NSLog(@'call dispatch_suspend suspends thread') } } //Stop ⏹ - (IBAction)stopTimer{ if (self.timer) { //EXC_BAD_INSTRUCTION //If it is suspended, call dispatch_source_cancel to crash // After the dispatch_suspend call, the timer cannot be released. Under normal circumstances, a crash will occur. //This is because the dispatch source release determines if it is currently paused. if (self.isSuspend) { dispatch_resume(self.timer) } self.isSuspend = NO dispatch_source_cancel(self.timer) self.timer = nil NSLog(@'call dispatch_source_cancel destroy resource') } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning] // Dispose of any resources that can be recreated. } - (void)dealloc{ NSLog (@'destroyed') } @end

【注意】

  • Dispatch_resumeとdispatch_suspendはペアで使用する必要がありますが、現在APIが一時停止または実行されていないため、自分で記録する必要があります。
  • 一時停止カウントカウンターが0で非アクティブ状態の場合、dispatch_resumeメソッドを呼び出すと、アサーションがトリガーされてプロセスが終了します。

【NSTimerとの比較】

  • GCDタイマーはRunLoopに依存せず、より時間厳守です。
  • [NSTimercheduledTimerWithTimeInterval:time target:selfセレクター:@selector(refresh)userInfo:nilrepeats:YES]繰り返しがYESの場合、自己参照は+1とカウントされるため、VCが解放されない場合があります。
  • NSTimerの作成と破棄は同じスレッドで行う必要があり、performSelectorの作成と取り消しは同じスレッドで行う必要があります。
  • タイマーに関係なく、使用しないときは破棄する必要があります。

結論:GCDには多くのAPIが使用されています。公式ドキュメントをご覧になることをお勧めします。
時間がかかるので、使用量を更新する時間があるまで待ちます。