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 

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


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

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

得到 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. 

实例:

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 的执行优先级作为线程的执行优先级使用。

各种 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 一起使用, 可实现高效率的数据库访问和文件访问

执行结果: 

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

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函数容易引起死锁

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

该源代码在 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读写文件时使用

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容