iOS GCD使用指南

Grand Central Dispatch(GCD)是异步运行任务的技术之中的一个。

一般将应用程序中记述的线程管理用的代码在系统级中实现。开发人员仅仅须要定义想运行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划运行任务。因为线程管理是作为系统的一部分来实现的,因此可统一管理。也可运行任务,这样就比曾经的线程更有效率。

Dispatch Queue

Dispatch Queue是用来运行任务的队列,是GCD中最主要的元素之中的一个。

Dispatch Queue分为两种:

  • Serial Dispatch Queue,按加入进队列的顺序(先进先出)一个接一个的运行
  • Concurrent Dispatch Queue,并发运行队列里的任务

简而言之,Serial Dispatch Queue仅仅使用了一个线程,Concurrent Dispatch Queue使用了多个线程(详细使用了多少个,由系统决定)。

能够通过两种方式来获得Dispatch Queue,第一种方式是自己创建一个:

let myQueue: dispatch_queue_t = dispatch_queue_create("com.xxx", nil)

第一个參数是队列的名称。通常是使用倒序的全域名。尽管能够不给队列指定一个名称,可是有名称的队列能够让我们在遇到问题时更好调试;当第二个參数为nil时返回Serial Dispatch Queue,如上面那个样例。当指定为DISPATCH_QUEUE_CONCURRENT时返回Concurrent Dispatch Queue。

须要注意一点,假设是在OS X 10.8或iOS 6以及之后版本号中使用,Dispatch Queue将会由ARC自己主动管理,假设是在此之前的版本号,须要自己手动释放,例如以下:

let myQueue: dispatch_queue_t = dispatch_queue_create("com.xxx", nil)

dispatch_async(myQueue, { () -> Void in

println("in Block")

})

dispatch_release(myQueue)

以上是通过手动创建的方式来获取Dispatch Queue。另外一种方式是直接获取系统提供的Dispatch Queue。

要获取的Dispatch Queue无非就是两种类型:

  • Main Dispatch Queue
  • Global Dispatch Queue / Concurrent Dispatch Queue

一般仅仅在须要更新UI时我们才获取Main Dispatch Queue。其它情况下用Global Dispatch Queue就满足需求了:

//获取Main Dispatch Queue

let mainQueue = dispatch_get_main_queue()

//获取Global Dispatch Queue

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

得到的Global Dispatch Queue实际上是一个Concurrent Dispatch Queue,Main Dispatch Queue实际上就是Serial Dispatch Queue(而且仅仅有一个)。

获取Global Dispatch Queue的时候能够指定优先级,能够依据自己的实际情况来决定使用哪种优先级。

普通情况下,我们通过另外一种方式获取Dispatch Queue即可了。

dispatch_after

dispatch_after能让我们加入进队列的任务延时运行,比方想让一个Block在10秒后运行:

var time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(10 * NSEC_PER_SEC))

dispatch_after(time, globalQueue) { () -> Void in

println("在10秒后运行")

}

NSEC_PER_SEC表示的是秒数。它还提供了NSEC_PER_MSEC表示毫秒。

上面这句dispatch_after的真正含义是在10秒后把任务加入进队列中,并非表示在10秒后运行,大部分情况该函数能达到我们的预期,仅仅有在对时间要求非常精准的情况下才可能会出现故障。

获取一个dispatch_time_t类型的值能够通过两种方式来获取,以上是第一种方式,即通过dispatch_time函数。还有一种是通过dispatch_walltime函数来获取,dispatch_walltime须要使用一个timespec的结构体来得到dispatch_time_t。

通常dispatch_time用于计算相对时间,dispatch_walltime用于计算绝对时间,我写了一个把NSDate转成dispatch_time_t的Swift方法:

func getDispatchTimeByDate(date: NSDate) -> dispatch_time_t {

let interval = date.timeIntervalSince1970

var second = 0.0

let subsecond = modf(interval, &second)

var time = timespec(tv_sec: __darwin_time_t(second), tv_nsec: (Int)(subsecond * (Double)(NSEC_PER_SEC)))

return dispatch_walltime(&time, 0)

}

这种方法接收一个NSDate对象,然后把NSDate转成dispatch_walltime须要的timespec结构体。最后再把dispatch_time_t返回,相同是在10秒后运行。之前的代码在调用部分须要改动成:

var time = getDispatchTimeByDate(NSDate(timeIntervalSinceNow: 10))

dispatch_after(time, globalQueue) { () -> Void in

println("在10秒后运行")

}

这就是通过绝对时间来使用dispatch_after的样例。

dispatch_group

可能经常会有这样一种情况:我们如今有3个Block要运行,我们不在乎它们运行的顺序,我们仅仅希望在这3个Block运行完之后再运行某个操作。

这个时候就须要使用dispatch_group了:

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

let group = dispatch_group_create()

dispatch_group_async(group, globalQueue) { () -> Void in

println("1")

}

dispatch_group_async(group, globalQueue) { () -> Void in

println("2")

}

dispatch_group_async(group, globalQueue) { () -> Void in

println("3")

}

dispatch_group_notify(group, globalQueue) { () -> Void in

println("completed")

}

输出的顺序与加入进队列的顺序无关。因为队列是Concurrent Dispatch Queue,但“completed”的输出一定是在最后的:

312
completed

除了使用dispatch_group_notify函数能够得到最后运行完的通知外。还能够使用

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

let group = dispatch_group_create()

dispatch_group_async(group, globalQueue) { () -> Void in

println("1")

}

dispatch_group_async(group, globalQueue) { () -> Void in

println("2")

}

dispatch_group_async(group, globalQueue) { () -> Void in

println("3")

}

//使用dispatch_group_wait函数

dispatch_group_wait(group, DISPATCH_TIME_FOREVER)

println("completed")

须要注意的是。dispatch_group_wait实际上会使当前的线程处于等待的状态,也就是说假设是在主线程运行dispatch_group_wait,在上面的Block运行完之前,主线程会处于卡死的状态。

能够注意到dispatch_group_wait的第二个參数是指定超时的时间,假设指定为DISPATCH_TIME_FOREVER(如上面这个样例)则表示会永久等待,直到上面的Block所有运行完。除此之外,还能够指定为详细的等待时间。依据dispatch_group_wait的返回值来推断是上面block运行完了还是等待超时了。

最后。同之前创建dispatch_queue一样,假设是在OS X 10.8或iOS 6以及之后版本号中使用。Dispatch Group将会由ARC自己主动管理。假设是在此之前的版本号,须要自己手动释放。

dispatch_barrier_async

dispatch_barrier_async就如同它的名字一样,在队列运行的任务中添加“栅栏”。在添加“栅栏”之前已经開始运行的block将会继续运行。当dispatch_barrier_async開始运行的时候其它的block处于等待状态,dispatch_barrier_async的任务运行完后。其后的block才会运行。我们简单的写个样例。假设这个样例有读文件和写文件的部分:

func writeFile() {

NSUserDefaults.standardUserDefaults().setInteger(7, forKey: "Integer_Key")

}

func readFile(){

print(NSUserDefaults.standardUserDefaults().integerForKey("Integer_Key"))

}

写文件仅仅是在NSUserDefaults写入一个数字7。读仅仅是将这个数字打印出来而已。我们要避免在写文件时候正好有线程来读取,就使用dispatch_barrier_async函数:

NSUserDefaults.standardUserDefaults().setInteger(9, forKey: "Integer_Key")

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_barrier_async(globalQueue) {self.writeFile() ; self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

我们先将一个9初始化到NSUserDefaults的Integer_Key中,然后在中间运行dispatch_barrier_async函数,因为这个队列是一个Concurrent Dispatch Queue,能同一时候并发多少线程是由系统决定的,假设加入dispatch_barrier_async的时候,其它的block(包含上面4个block)还没有開始运行。那么会先运行dispatch_barrier_async里的任务,其它block所有处于等待状态。假设加入dispatch_barrier_async的时候,已经有block在运行了,那么dispatch_barrier_async会等这些block运行完后再运行。

dispatch_apply

dispatch_apply会将一个指定的block运行指定的次数。假设要对某个数组中的所有元素运行相同的block的时候。这个函数就显得非常实用了,使用方法非常easy,指定运行的次数以及Dispatch Queue。在block回调中会带一个索引,然后就能够依据这个索引来推断当前是对哪个元素进行操作:

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

dispatch_apply(10, globalQueue) { (index) -> Void in

print(index)

}

print("completed")

因为是Concurrent Dispatch Queue,不能保证哪个索引的元素是先运行的。可是“completed”一定是在最后打印。因为dispatch_apply函数是同步的。运行过程中会使线程在此处等待。所以一般的,我们应该在一个异步线程里使用dispatch_apply函数:

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

dispatch_async(globalQueue, { () -> Void in

dispatch_apply(10, globalQueue) { (index) -> Void in

print(index)

}

print("completed")

})

print("在dispatch_apply之前")

dispatch_suspend / dispatch_resume

某些情况下,我们可能会想让Dispatch Queue临时停止一下,然后在某个时刻恢复处理,这时就能够使用dispatch_suspend以及dispatch_resume函数:

//暂停

dispatch_suspend(globalQueue)

//恢复

dispatch_resume(globalQueue)

暂停时。假设已经有block正在运行。那么不会对该block的运行产生影响。dispatch_suspend仅仅会对还未開始运行的block产生影响。

Dispatch Semaphore

信号量在多线程开发中被广泛使用,当一个线程在进入一段关键代码之前。线程必须获取一个信号量,一旦该关键代码段完毕了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待前面的线程释放信号量。

信号量的详细做法是:当信号计数大于0时,每条进来的线程使计数减1,直到变为0。变为0后其它的线程将进不来。处于等待状态;运行完任务的线程释放信号,使计数加1。如此循环下去。

以下这个样例中使用了10条线程,可是同一时候仅仅运行一条,其它的线程处于等待状态:

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

let semaphore =  dispatch_semaphore_create(1)

for i in 0 ... 9 {

dispatch_async(globalQueue, { () -> Void in

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

let time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(2 * NSEC_PER_SEC))

dispatch_after(time, globalQueue) { () -> Void in

print("2秒后运行")

dispatch_semaphore_signal(semaphore)

}

})

}

取得信号量的线程在2秒后释放了信息量,相当于是每2秒运行一次。

通过上面的样例能够看到,在GCD中,用dispatch_semaphore_create函数能初始化一个信号量,同一时候须要指定信号量的初始值;使用dispatch_semaphore_wait函数分配信号量并使计数减1,为0时处于等待状态;使用dispatch_semaphore_signal函数释放信号量。并使计数加1。

另外dispatch_semaphore_wait相同也支持超时。仅仅须要给其第二个參数指定超时的时候即可,同Dispatch Group的dispatch_group_wait函数相似,能够通过返回值来推断。

这个函数也须要注意,假设是在OS X 10.8或iOS 6以及之后版本号中使用。Dispatch Semaphore将会由ARC自己主动管理,假设是在此之前的版本号,须要自己手动释放。

dispatch_once

dispatch_once函数通经常使用在单例模式上,它能够保证在程序运行期间某段代码仅仅运行一次,假设我们要通过dispatch_once创建一个单例类,在Swift能够这样:

class SingletonObject {

class var sharedInstance : SingletonObject {

struct Static {

static var onceToken : dispatch_once_t = 0

static var instance : SingletonObject? = nil

}

dispatch_once(&Static.onceToken) {

Static.instance = SingletonObject()

}

return Static.instance!

}

}

这样就能通过GCD的安全机制保证这段代码仅仅运行一次。

时间: 2024-10-13 03:32:23

iOS GCD使用指南的相关文章

iOS GCD的常见API使用指南

iOS GCD使用指南 Grand Central Dispatch(GCD)是异步执行任务的技术之一.一般将应用程序中记述的线程管理用的代码在系统级中实现.开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务.由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样就比以前的线程更有效率. Dispatch Queue Dispatch Queue是用来执行任务的队列,是GCD中最基本的元素之一. Dispatch

线程同步-iOS多线程编程指南(四)-08-多线程

首页 编程指南 Grand Central Dispatch 基本概念 多核心的性能 Dispatch Sources 完结 外传:dispatch_once(上) Block非官方编程指南 基础 内存管理 揭开神秘面纱(上) 揭开神秘面纱(下) iOS多线程编程指南 关于多线程编程 线程管理 Run Loop 线程同步 附录 Core Animation编程指南 Core Animation简介 基本概念 渲染架构 几何变换 查看目录 中文手册/API ASIHTTPRequest Openg

iOS GCD中级篇 - dispatch_group的理解及使用

前文我们讲了GCD基础篇,以及同步.异步,并发.并行几个概率的理解. 参考链接: iOS GCD基础篇 - 同步.异步,并发.并行的理解 现在讲一下dispatch_group的概念以及几种场景下的使用 1.关于dispatch_group 把一组任务提交到队列中,这些队列可以不相关,然后监听这组任务完成的事件. 最常见的几个方法: 1.dispatch_group_create创建一个调度任务组 2.dispatch_group_async 把一个任务异步提交到任务组里 3.dispatch_

IOS GCD

UIButton * btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; [btn setTitle:@"default"forState:UIControlStateNormal]; btn.backgroundColor = [UIColorblackColor]; [self.view addSubview:btn]; //情况一 dispatch_async(dispatch_get_glo

技术福利【干货】音视频 iOS CallKit 开发指南

iOS CallKit 开发指南 简介 CallKit 是融云音视频通话功能的 UI 界面 SDK.包含了单人.多人音视频通话的界面的各种场景和功能.您可以快速的集成 CallKit 来实现丰富的音视频通话界面,并进行自己的 UI 定制开发.同时我们开源了 CallKit,您可以根据您的需要去使用. GitHub 项目:CallKit 开源代码 使用说明 由于底层引擎技术不同,2.6.0 之后的音视频 SDK 与 2.6.0 之前的 SDK 中的 VoIP 不能互通. 音视频 SDK 为商用收费

(译)IOS block编程指南 1 介绍

Introduction(介绍) Block objects are a C-level syntactic and runtime feature. They are similar to standard C functions, but in addition to executable code they may also contain variable bindings to automatic (stack) or managed (heap) memory. A block ca

李洪强iOS之集成极光推送二iOS 证书 设置指南

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px "PingFang SC"; color: #000000 } span.s1 { } span.s2 { font: 18.0px Menlo } 创建应用程序ID 登陆 iOS Dev Center 选择进入iOS Provisioning Portal. 在 iOS Provisioning Portal中,点击App IDs进入App ID列表. 创建 App ID,如果

iOS GCD学习

GCD概念:Grand Central Dispatch 1.queue 队列 dispatch queue 分发队列 2.task  任务 3.thread 线程 task 是一个一个单独的任务(方法,函数,block) queue里存放的是一个或者多个task thread 为了保证task能顺利执行,queue会开辟适当的thread,在开辟的thread里面执行task queue分两种: 1.serialqueue(串行队列) 特点:任务先进先出.排在前面任务先执行,执行之后后面的任务

推荐——Monkey《大话 app 测试——Android、iOS 应用测试指南》

<大话移动——Android与iOS应用测试指南> 京东可以预购啦!http://item.jd.com/11495028.html 当当网:http://product.dangdang.com/23510301.html#catalog 大家速度预购哈--- 这本书离不开大家的支持.其中感谢给我写书评的几位大牛.@熊力_LiXiong @阿里窥基 @徐毅-Kaveri @左耳朵耗子 .明天京东的链接也出来啦-- 推荐--Monkey<大话 app 测试--Android.iOS 应用