Objective-C高级编程:iOS与OS X多线程和内存管理(三)

1 GCD 概要

1.1 什么是 GCD

GCD 是异步执行任务的技术之一。实现了极为复杂繁琐的多线程编程。

在导入 GCD 之前,Cocoa 框架提供了 NSObject 类的 performSelectorInBackground:withObject 方法 和 performSelectorOnMainThread 方法等简单的多线程技术。

2 GCD 的 API

2.1 Dispatch Queue

Dispatch Queue是执行处理的等待队列。按照追回的顺序(先进先出) 执行处理

存在两种 Dispatch Queue:

一种:Serial Dispatch Queue 

等待现在执行的处理结束 -> 执行顺序固定

另一种:Concurrent Dispatch Queue 

不等待现在执行的处理结束 -> 执行顺序会根据处理内容和系统状态发生改变。 

Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第1张图片


Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第2张图片

并行执行的处理数量取决于当前系统的状态。即系统中基于 Dispatch Queue 中的处理数、CPU 核数以及 CPU 负荷等当前系统的状态来决定 Concurrent Dispatch Queue 中并行执行的处理数。

所谓 “并行执行”,就是使用多个线程同时执行多个处理。 

Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第3张图片

得到 Dispatch Queue 的两种方法:

第一种:通过 GCD 的 API 生成 Dispatch Queue。 (dispatch_queue_create)

第二种:获取系统标准提供的 Dispatch Queue

2.2 dispatch_queue_create

得到 Dispatch Queue 的两种方法:

第一种:通过 GCD 的 API 生成 Dispatch Queue。 (dispatch_queue_create)

用 dispatch_queue_create 可以生成多个 Dispatch Queue.

一旦生成 Serial Dispatch Queue 并追加处理,系统对于一个 Serial Dispatch Queue 就只生成并使用一个线程。有多少个 Serial Dispatch Queue 就生成多少个线程。

注:过多使用线程,就会消耗大量内存。

为了避免多线程编程问题之一 ——多个线程更新相同资源导致数据竞争时使用Serial Dispatch Queue. 

Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第4张图片

实例:

dispatch_queue_t  mySerialDispatchQueue = 

dispatch_queue_create(“com.example.gcd.MySerialDispatchQueue”, NULL);

dispatch_queue_create 函数:

第一个参数:指定 Serial Dispatch Queue 的名称  推荐使用 -> 应用程序 ID 逆序全程域名。

第二个参数:指定为 NULL

dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create(

“com.example.gcd.MyConcurrentDispatchQueue”,  DISPATCH_QUEUE_CONCURRENT);

Dispatch Queue 必须由程序员负责释放。通过 dispatch_queue_create 函数生成的 Dispatch Queue 在使用结束后通过 dispatch_release 函数释放。 [现在 ARC GCD 不需要 dispatch_release ]

在 dispatch_async 函数中追加 Block 到 Dispatch Queue 后,即使立即释放 Dispatch Queue, 该 Dispatch Queue 由于被 Block 所持有也不会被废弃,因而 Block 能够执行。Block 执行结束后会释放 Dispatch Queue,这时谁都不持有 Dispatch Queue,因此它会被废弃。

2.3 Main Dispatch Queue / Global Dispatch Queue

第二种:获取系统标准提供的 Dispatch Queue

Main Dispatch Queue : 在主线程中执行。同 NSObject 类的 performSelectorOnMainThread 方法。

Global Dispatch Queue :是所有应用程序都能够使用的 Concurrent Dispatch Queue.

Global Dispatch Queue 有4个执行优先级:

将使用的 Global Dispatch Queue 的执行优先级作为线程的执行优先级使用。

Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第5张图片

各种 Dispatch Queue 的获取方式:

dispatch_get_main_queue();

dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_LOW, 0);

dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

获取系统提供的 Main Dispatch Queue / Global Dispatch Queue 不需要内存管理。

2.4 dispatch_set_target_queue

dispatch_set_target_queue : 用于变更 Dispatch Queue 的执行优先级

dispatch_queue_create 函数生成的 Dispatch queue 都是 默认优先级(Default Priority)

dispatch_set_target_queue (参数1, 参数2);

参数1:要变更执行优先级的 Dispatch Queue

参数2:指定为要使用的执行优先级相同优先级的 Global Dispatch Queue

dispatch_set_target_queue 函数,不仅可以变量 Dispatch Queue 的执行优先级,还可以作成 Dispatch Queue 的执行阶层。

例:在必须将不可并行执行的处理追回到多个 Serial Dispatch Queue 中时,如果使用 dispatch_set_target_queue 函数将目标指定为某一个 Serial Dispatch Queue, 即可防止处理并行执行。

2.5 dispatch_after

 dispatch_after:在指定时间后执行处理

注:dispatch_after 函数并不是在指定时间后执行处理,而只是在指定时间追回处理到 Dispatch Queue

ull :unsigned long long

DISPATCH_TIME_NOW :表示现在的时间

获取 dispatch_time_t 对象的两种方法

        第一种:dispatch_time 从第一个参数指定的时间开始,到第二个参数指定的毫微秒单位时间后的时间。

        第二种:dispatch_wallTime 通常用于计算绝对时间,可以作为粗略的闹钟功能使用

2.6 Dispatch Group

Dispatch Group使用在什么情况下

在追加到 Dispatch Queue 中的多个处理全部结束后想执行结束处理,这种情况下使用 Dispatch Group。

用 dispatch_group_create 函数生成的 dispatch_group_t 类型的 Dispatch Group 使用结束后需要通过 dispatch_release 函数释放 [现在 ARC 会自动释放]

dispatch_group_async (group, queue, ^{…});

dispatch_group_async函数,追加 Block 到指定的 Dispatch Queue 中。第一个参数:指定生成的Dispatch Group,  指定的 Block 属于指定的 Dispatch Group.

为什么指定的 Block 属于指定的 Dispatch Group 以及 Dispatch Group 的释放问题

追加 Block 到 Dispatch Queue 的同时, Block 通过 dispatch_retain 函数持有 Dispatch Group,从而使得该 Block 属于 Dispatch Group。这样如果 Block 执行结束,该 Block 就通过 dispatch_release 函数释放持有的 Dispatch Group。 一旦 Dispatch Group 使用结束,不用考虑属于该 Dispatch Group 的 Block,立即通过 dispatch_release 函数释放即可。

无论向什么样的 Dispatch Queue 中追加处理,使用 Dispatch Group 都可监视这些处理执行的结束。一旦检测到所有处理执行结束,就可将结束的处理追加到 Dispatch Queue 中。

在 Dispatch Group 中使用的函数:

dispatch_group_notify : 

dispatch_group_notify (group, dispatch_get_main_queue(), ^{…});

在追加到 Dispatch Group 中的处理全部执行结束时,dispatch_group_notify 函数会将执行的 Block 追回到 Dispatch Queue 中。

dispatch_group_wait :  等待全部处理执行结束。

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

第二个参数:指定为等待的时间(超时),属于 dispatch_time_t 类型的值

返回值:0 与 非0

返回值不为0: 虽然经过了指定的时间,但属于 Dispatch Group 的某一个处理还在执行中。

返回0: 全部处理执行结束。

“等待”是什么意思:

一旦调用 dispatch_group_wait 函数,该函数就处于调用的状态而不返回。即执行 dispatch_group_wait 函数的现在的线程停止。在经过 dispatch_group_wait 函数中指定的时间或属于指定 Dispatch Group 的处理全部执行结束之前,执行该函数的线程停止。

2.7 dispatch_barrier_async

dispatch_barrier_async用于对文件写入数据

dispatch_barrier_async与 Concurrent Dispatch Queue 一起使用, 可实现高效率的数据库访问和文件访问

Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第6张图片

执行结果: 

Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第7张图片

dispatch_barrier_async 函数会等待追回到 Concurrent Dispatch Queue 上的并行执行的处理全部结束之后,再将指定的处理追回到该 Concurrent Dispatch Queue 中。然后在由 dispatch_barrier_async 函数追回的处理执行完毕后, Concurrent Dispatch Queue 才恢复为一般的动作,追回到该 Concurrent Dispatch Queue 的处理又开始并执行。

Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第8张图片

2.8 dispatch_sync

dispatch_sync :将指定的 Block “同步” 追回到指定的 Dispatch Queue 中。在追回 Block 结束之前,dispatch_sync 函数会一直等待。

什么情况下使用 dispatch_sync ?

例如:执行 Main Dispatch Queue 时,使用另外的线程 Global Dispatch Queue 进行处理,处理结束后立即使用所得到的结果。

一旦调用 dispatch_sync 函数,那么在指定的处理执行结束之前,该函数不会返回。dispatch_sync 函数可简化源代码,也可说是简易版的 dispatch_group_wait 函数。

dispatch_sync函数容易引起死锁

例如:在主线程中执行以下代码就会死锁 

Objective-C高级编程:iOS与OS X多线程和内存管理(三)_第9张图片

该源代码在 Main Dispatch Queue 即主线程中执行指定的 Block,并等待其执行结束。而其实在主线程中正在执行这些源代码,所以无法执行追加到 Main Dispatch Queue 的 Block。

2.9 dispatch_apply

dispatch_apply :是 dispatch_sync 函数和 Dispatch Group 的关联 API。该函数按指定的次数将指定的 Block 追回到指定的 Dispatch Queue 中,并等待全部处理执行结束。

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_apply(10, queue, ^(size_t index) {

        //size_t : unsigned long;  %zu用来输出 size_t 类型

        NSLog(@"%zu", index);

    });

    NSLog(@“done");

输出结果中最后的 done 必定在最后的位置上。这是因为 dispatch_apple 函数会等待全部处理执行结果。

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

参数1:重复次数

参数2:追加对象的  Dispatch Queue

参数3:追加的处理 Block,Block 为带参数的 Block,为了按第1个参数重复追加 Block 并区分各个 Block 而使用。

2.10 dispatch_suspend / dispatch_resume

dispatch_suspend :挂起指定的 Dispatch Queue

dispatch_resume :恢复指定的 Dispatch Queue

这些函数对已经执行的处理没有影响。挂起后,追加到 Dispatch Queue 中但尚未执行的处理在此之后停止执行。而恢复则使得这些处理能够继续执行。

2.11 Dispatch Semaphore

Dispatch Semaphore是持有计数的信号,该计数是多线程编程中的计数类型信号。计数为0时等待,计数为1或大于1时,减去1而不等待。

2.12 dispatch_once

dispatch_once:在单例模式,生成单例对象时使用。

2.13 Dispatch I/O

Dispatch I/O读写文件时使用

你可能感兴趣的:(Objective-C高级编程:iOS与OS X多线程和内存管理(三))