iOS GCD使用指南
Grand Central Dispatch(GCD)是异步运行任务的技术之中的一个。
一般将应用程序中记述的线程管理用的代码在系统级中实现。开发人员仅仅须要定义想运行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划运行任务。因为线程管理是作为系统的一部分来实现的,因此可统一管理。也可运行任务,这样就比曾经的线程更有效率。
Dispatch Queue
Dispatch Queue是用来运行任务的队列,是GCD中最主要的元素之中的一个。
Dispatch Queue分为两种:
- Serial Dispatch Queue,按加入进队列的顺序(先进先出)一个接一个的运行
- Concurrent 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
//获取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(而且仅仅有一个)。
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
这个时候就须要使用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")
}
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_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
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_suspend(globalQueue)
//恢复
dispatch_resume(globalQueue)
暂停时。假设已经有block正在运行。那么不会对该block的运行产生影响。dispatch_suspend仅仅会对还未開始运行的block产生影响。
Dispatch Semaphore
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秒运行一次。
dispatch_once
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的安全机制保证这段代码仅仅运行一次。
iOS GCD使用指南的更多相关文章
- swift GCD使用指南
swift GCD使用指南 Grand Central Dispatch(GCD)是异步执行任务的技术之一.一般将应用程序中记述的线程管理用的代码在系统级中实现.开发者只需要定义想执行的任务并追加到适 ...
- 《大话移动APP测试:Android与iOS应用测试指南》
<大话移动app测试:android与ios应用测试指南> 基本信息 作者: 陈晔 出版社:清华大学出版社 ISBN:9787302368793 上架时间:2014-7-7 出版日期:20 ...
- 推荐——Monkey《大话 app 测试——Android、iOS 应用测试指南》
<大话移动——Android与iOS应用测试指南> 京东可以预购啦!http://item.jd.com/11495028.html 当当网:http://product.dangdang ...
- iOS GCD基础篇 - 同步、异步,并发、并行的理解
1.关于GCD - GCD全称是Grand Central Dispatch - GCD是苹果公司为多核的并行运算提出的解决方案 - GCD会自动利用更多的CPU内核(比如双核.四核) - GC ...
- IOS设备设计完整指南
作为初学者,常常不知如何下手设计,IOS应用UI设计中碰到的种种基础小问题,在此都将一一得到解答.这份完整的设计指南将带你快速上手,为IOS设计出优雅的应用吧. 关于此设计指南 此设计指南描述的是如何 ...
- 李洪强iOS之集成极光推送二iOS 证书 设置指南
李洪强iOS之集成极光推送二iOS 证书 设置指南 创建应用程序ID 登陆 iOS Dev Center 选择进入iOS Provisioning Portal. 在 iOS Provisioning ...
- iOS GCD之dispatch_semaphore(信号量)
前言 最近在看AFNetworking3.0源码时,注意到在 AFURLSessionManager.m 里面的 tasksForKeyPath: 方法 (L681),dispatch_semapho ...
- iOS多线程编程指南
iOS多线程编程指南(拓展篇)(1) 一.Cocoa 在Cocoa上面使用多线程的指南包括以下这些: (1)不可改变的对象一般是线程安全的.一旦你创建了它们,你可以把这些对象在线程间安全的传递.另一方 ...
- (译)IOS block编程指南 1 介绍
Introduction(介绍) Block objects are a C-level syntactic and runtime feature. They are similar to stan ...
随机推荐
- 应用JavaScript搭建一个简易页面图片无缝滚动效果
页面图片无缝滚动JavaScript原理:移动的区块包含图片内容,区块相对父级元素进行定位脱离文档流.再令区块的left值每隔固定的时间进行等量减少(或增大)从而实现区块的匀速运动.由于每次间隔移动的 ...
- asp.net MVC 路由注册
1.命名空间的优先级 在路由注册时指定的命名空间比当前 ControllerBuilder 的默认命名空间具有更高的匹配优先级,但是对于这两个集合中的所有命名空间却具有相同的匹配优先级.换句话说,用于 ...
- VC常用代码之创建进程
作者:朱金灿 来源:http://blog.csdn.net/clever101 创建进程是编程开发的常用操作.Windows中的创建进程采用API函数CreateProcess实现.下面是一个使用例 ...
- angular2之组件通讯
定义父组件,在父组件中以路由插座形式引入子组件,定义相关输入输出属性 可以在同一模块内部定义多个组件,将一个组件引入另一个组件中去:也可以该模块整体导出,将该模块导入到其他模块,这样此模块中的组件就能 ...
- dubbo之泛化实现
实现泛化调用 泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 Gene ...
- 学习廖雪峰的Python教程之数据类型
数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等各种各样的数据,不同的数据,需要定 ...
- redis 主从复制 redis 探索
http://blog.csdn.net/column/details/12804.html
- POJ_2115_扩展欧几里德
C Looooops Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 23673 Accepted: 6540 Descr ...
- HDU_5833_高斯消元
参考自:http://www.cnblogs.com/flipped/p/5771492.html 自己做的时候不知道如何求种数.看了题解,感觉思路灰常巧妙.同时也感觉这是一道好题. 精髓在于转化为线 ...
- github+hexo(window10)
一.申请github账户 二.先安装node.js.git 本地: 三.安装hexo(建立静态网页,用Markdown写博客) 1.创建文件地址 在合适的地方新建一个文件夹,用来存放自己的博客文件,比 ...