iOS学习之GCD
多线程编程
线程定义:一个CPU执行的CPU命令 列一条无分叉的路径就叫线程。
多线程:执行多个不同的CPU命令 有多条路径。
线程的使用:主线程(又叫作UI线程)主要任务是处理UI事件,显示和刷新UI,(只有主线程有直接修改UI的能力)耗时的操作放在子线程(又叫作后台线程、异步线程)。
多线程容易引发的编程问题:
- 数据竞争:多个线程更新相同的资源会导致数据不一致。
- 死锁:停止等待事件的线程会导致多个线程相互持续等待。
- 太多线程会消耗大量内存。
- Thread Safe(线程安全):一段线程安全的代码(对象),可以同时被多个线程或并发的任务调度,不会产生问题,非线程安全的只能按次序被访问。
多线程优点:在需要常实际处理方法放到其他线程中,让其他线程处理,主线程不处理。
在GCD之前的多线程方法:NSThread, NSOperationQueue, NSInvocationOperation
GCD:Grand Central Dispatch:异步执行任务的技术。
没有使用GCD时,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是在使用GCD后,app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。
方法优缺点分析:
- NSThread (抽象层次:低)
- 优点:轻量级,简单易用,可以直接操作线程对象
- 缺点: 需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销。
- Cocoa NSOperation(抽象层次:中)
- 优点:不需要关心线程管理,数据同步的事情,可以把精力放在学要执行的操作上。基于GCD,是对GCD 的封装,比GCD更加面向对象
- 缺点: NSOperation是个抽象类,使用它必须使用它的子类,可以实现它或者使用它定义好的两个子类NSInvocationOperation、NSBlockOperation。
- GCD抽象层次:高)
- 优点:是 Apple 开发的一个多核编程的解决方法,简单易用,效率高,速度快,基于C语言,更底层更高效,并且不是Cocoa框架的一部分,自动管理线程生命周期(创建线程、调度任务、销毁线程)。
- 缺点: 使用GCD的场景如果很复杂,就有非常大的可能遇到死锁问题。
GCD的优势
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
在GCD之前CoCoa框架提供了NSObject类的 performSelectorInBackgroud:withObject实例方法和performSelectorOnMainThread实例方法。
- PerformSelector在内存管理方面会很容易有疏失,它无法确定将要执行的selector具体是什么,所以ARC没办法插入适当的内存管理方法
SEL selector;
If( ){
Selector=@ selector(newObject)}
Else if(){
Selector=@ selector(copy)}
Else {
Selector=@ selector(somesasr)}
Id ret=[object performSelector:selector // 这样编译器不知道将要执行的seletor是哪个,必须到运行期才能确定,ARC没办法插入适当的内存管理方法。会出现错误警告。
];
//前两个需要自己释放,后面一个不需要自己释放,使用ARC会出现错误警告,不使用ARC很容易忽视掉,就算使用静态分析其,也难侦测到
- 处理的东西太过于局限,返回值类型及发送给方法的参数个数都受到限制。
- 如果想要把任务放在另一个线程上执行,最好不要用performSelector方法,应该封装到block中,然后调用GCD来实现。
performSelector跟GCD处理同一个任务:
)[self performSelector:@selector(dosomething)
withObject:nil
after:5.0];// performSelector
)dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(5.0*NSEC_PER_SEC));
Dispatch_after(time,dispatch_get_main_queue(),^(void){
[self doSomething]; });//GCD
系统提供的Dispatch方法:
- 后台执行:
dispatch_async(dispatch_get_global_queue(0,0),^{
//something
});
- 主线程执行:
dispatch_async(dispatch_get_main_queue(),^{
//something
});
- 一次性执行:dispatch_once保证在app运行期间,block中的代码只执行一次
- 经典使用场景---单例
- 单例对象ShareManager的定义:
Static dispatch_once_t onceToken;
dispatch_once(&onceToken ,^{
//something
});
- 延迟2秒执行:
Double delayInSeconds=2.0;
Dispatch_time_t popTime=dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
Dispatch_after(popTime, dispatch_get_main_queue(),^(void){
// onceToken
});
- 自定义执行:
要自定义queue可以用dispatch_queue_create
祥解GCD 的API
说明:只要将想执行的任务加入到适当的Dispatch Queue中。
两种Dispatch Queue:(Concurrent Dispatch Queue)
- Serial Dispatch Queue串行队列(图1): 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
Serial Dispatch Queue太多容易消耗太多内存。
所以只在可能数据竞争时使用Serial Dispatch Queue。
- Concurrent Dispatch Queue并发队列(图2):不等待现在执行中处理结束(可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效)一个线程可同时处理多个任务。在不发生数据竞争时都可用。
- Dispatch_queue_create
- 功能:创建自定义列队
- 使用方法:dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); // 队列名称, 队列属性,一般用NULL
dispatch_queue_t myQueue =dispatch_queue_create(com.expample.www,DISPATCH_QUEUE_CONCURRENT);
释放:dispatch_release(myQueue);
- 注意点:队列名称推荐使用应用程序ID的逆序全程域名(FQDN,fully qualified domain name)如:com.expample.www
由于在Xcode和Instruments的调试器中队列名称被用于Dispatch Queue名称表示。可以方便寻找。
通过Dispatch Queue创建的对列在结束使用时候要使用dispatch_release释放。
- Main Dispatch Queue/ Global Dispatch Queue
- Main Dispatch Queue属于Serial Dispatch Queue 类型。
- Global Dispatch Queue属于 Concurrent Dispatch Queue类型,有四种优先级:High priority(高优先级)、Default priority(默认优先级),Low priority(低优先级),Background priority(后台优先级)
- Dispatch_after
- 功能:让任务延时添加到队列,将一个Block在特定的延时以后,加入到指定的队列中,不是在特定的时间后立即运行
- 使用方法:
dispatch_time_t delayTime3 = dispatch_time(DISPATCH_TIME_NOW, *NSEC_PER_SEC);
dispatch_time_t delayTime2 = dispatch_time(DISPATCH_TIME_NOW, *NSEC_PER_SEC);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"current task");
dispatch_after(delayTime3, mainQueue, ^{
NSLog(@"10秒之后添加到队列");
});
dispatch_after(delayTime2, mainQueue, ^{
NSLog(@"2秒之后添加到队列");
});
NSLog(@"next task");
输出结果:
-- ::19.369 Whisper[:] current task
-- ::19.370 Whisper[:] next task
-- ::21.369 Whisper[:] 2秒之后添加到队列
-- ::29.654 Whisper[:] 3秒之后添加到队列
- 注意点:dispatch_after只是延时提交block,并不是延时后立即执行
- dispatch_time_t
- 功能: 创建延时时间
使用方法:dispatch_time_t dispatch_time ( dispatch_time_t when, int64_t delta );
第一个参数一般是DISPATCH_TIME_NOW,表示从现在开始。
那么第二个参数就是真正的延时的具体时间。delta参数是“纳秒!
- dispatch_time_t delayTime3 = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
注意点:延时1秒可以写成如下几种:
dispatch_time(DISPATCH_TIME_NOW, * NSEC_PER_SEC); dispatch_time(DISPATCH_TIME_NOW, * USEC_PER_SEC); dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);
最后一个“USEC_PER_SEC * NSEC_PER_USEC”,翻译过来就是“每秒的毫秒数乘以每毫秒的纳秒数”,也就是“每秒的纳秒数”,所以,延时500毫秒之类的
- NSEC_PER_SEC,每秒有多少纳秒。
- USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上)
- NSEC_PER_USEC,每毫秒有多少纳秒。
- Dispatch Group:dispatch_group_async
- 功能:当遇到需要执行多个线程并发执行,然后等多个线程都结束之后,再汇总执行结果时可以用group queue(同时下载多个图片,所有图片下载完成之后去更新UI(需要回到主线程)或者去处理其他任务(可以是其他线程队列)。)
- 使用方法:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"picture1 download complete ");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"picture2 download complete ");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"picture3 download complete");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"update Ui");
});
dispatch_release(group);运行思路:使用函数dispatch_group_create创建dispatch group,然后使用函数dispatch_group_async来将要执行的block任务提交到一个dispatch queue。同时将他们添加到一个组,等要执行的block任务全部执行完成(三张图片都下载完成)之后,使用dispatch_group_notify函数接收完成时的消息(回到主线程:更新UI)。
运行结果:
::16.737 gcdTest[:] picture1 download complete
::17.738 gcdTest[:12a1b] picture2 download complete
::18.738 gcdTest[:] picture3 download complete
::18.739 gcdTest[:f803] update Ui
- 注意点:
- Dispatch_barrier_async:
- 功能:在它前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
- 使用方法:
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"dispatch_async1");
}); //第一个队列
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"dispatch_async2");
}); 第二个队列
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:]; }); // barrier队列
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"dispatch_async3");
}); // 第四个队列
运行思路:第一二个block同时运行,等一二block都运行完开始运行barrier队列,barrier队列运行完后,运行第四个block
运行结果:16:20:31开始运行
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"dispatch_async1");
}); //第一个队列
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"dispatch_async2");
}); 第二个队列
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:];
}); // barrier队列
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"dispatch_async3");
}); // 第四个队列
- 注意点:
- Dispatch_sync
- 功能:
- 使用方法:
- 注意点:
- Dispatch_apply
- 功能:把一项任务提交到队列中多次执行,队列可以是串行也可以是并行,dispatch_apply不会立刻返回,而是在执行完block中的任务后才会返回,是同步执行的函数。
- 使用方法:
dispatch_queue_t globalQueue = dispatch_get_global_queue(, );
NSLog(@"current task");
dispatch_async(globalQueue, ^{
dispatch_queue_t applyQueue = dispatch_get_global_queue(, );
//第一个参数,3--block执行的次数
//第二个参数,applyQueue--block任务提交到的队列
//第三个参数,block--需要重复执行的任务
dispatch_apply(, applyQueue, ^(size_t index) {
NSLog(@"current index %@",@(index));
sleep();
});
NSLog(@"dispatch_apply 执行完成");
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
NSLog(@"回到主线程更新UI");});
});
NSLog(@"next task");- 注意点:在主线程直接调用dispatch_apply会阻塞主线程,为了不阻塞主线程,一般把dispatch_apply放在异步队列中调用,然后执行完成后通知主线程
- Dispatch_suspend/dispatch_resume
- 功能:提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的block
使用方法:
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
//提交第一个block,延时5秒打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"Block2 printf after 5 seconds...");});
//提交第二个block,延时15秒打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"Block1 printf after 15 seconds ...");
});
//延时一秒
NSLog(@"sleep 1 second...");
[NSThread sleepForTimeInterval:];
//挂起队列
dispatch_suspend(queue);
//延时10秒
NSLog(@"sleep 10 second...");
[NSThread sleepForTimeInterval:];
//恢复队列
NSLog(@"resume...");
dispatch_resume(queue);
运行思路:
1.block开始运行(延时五秒打印),同时打印sleep 1 second...,
2.遇到延时一秒命令延时了一秒,运行到挂起命令,队列将暂停,但是正在运行的block1持续运行,在运行期五秒后打印Block1 printf after 5 seconds...
3.运行完block1,队列真正暂停。然后运行打印 sleep 10 second...之后到延时十秒命令,延时了十秒,十秒后运行打印;resume...;到恢复队列命令,队列恢复运行(block2开始运行),15秒后block2运行完毕,打印Block2 printf after 15 seconds ...
所以在dispatch_suspend挂起队列后,第一个block还是在运行,并且正常输出。
运行结果:
::09.903 sleep second...
::10.910 suspend...
::10.910 sleep second...
::14.908 Block1 printf after seconds...(block1持续运行)
::20.911 resume...
::35.912 Block2 printf after seconds ...
注意点:dispatch_suspend并不会立即暂停正在运行的block,而是在当前block执行完成后,暂停后续的block执行。所以想暂停正在队列上运行的block时,还是不要用dispatch_suspend
- Dispatch_once
- 功能:被广泛使用在单例、缓存等代码中,用以保证在初始化时执行一次某任务。在单线程程序中毫无意义,但在多线程程序中,其低负载、高可依赖性、接口简单等特性
- 使用方法:
- static dispatch_once_t once;
- dispatch_once(&once, ^{
- //单例代码
- });
- 注意点:dispatch_once_t必须是全局或static变量
让程序在后台长久运行的示例代码:
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;
// AppDelegate.m文件
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self beingBackgroundUpdateTask];
// 在这里加上你需要长久运行的代码
[self endBackgroundUpdateTask];
}
- (void)beingBackgroundUpdateTask
{
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void)endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
iOS学习之GCD的更多相关文章
- IOS学习之十七:Grand Central Dispatch(GCD)编程基础
IOS学习之十七:Grand Central Dispatch(GCD)编程基础 有过编程经验的人,基本都会接触到多线程这块. 在java中以及Android开发中,大量的后台运行,异步消息队列, ...
- iOS学习路线图
一.iOS学习路线图 二.iOS学习路线图--视频篇 阶 段 学完后目标 知识点 配套学习资源(笔记+源码+PPT) 密码 基础阶段 学习周期:24天 学习后目标: ...
- iOS学习资源个人整理
1208更新: http://www.tuyiyi.com 图翼网 https://github.com/Alamofire/Al ...
- iOS学习笔记-精华整理
iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...
- iOS学习笔记总结整理
来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...
- 2015最新iOS学习线路图
iOS是由苹果公司开发的移动操作系统,以xcode为主要开发工具,具有简单易用的界面.令人惊叹的功能,以及超强的稳定性,已经成为iPhone.iPad 和iPod touch 的强大基础:iOS 内置 ...
- ios 学习路线总结
学习方法 面对有难度的功能,不要忙着拒绝,而是挑战一下,学习更多知识. 尽量独立解决问题,而不是在遇到问题的第一想法是找人. 多学习别人开源的第三方库,能够开源的库一定有值得学习的地方,多去看别的大神 ...
- [iOS]深入理解GCD
看到一篇很好的文章,本来想翻译的,但发现已经有人翻译了,遂简单整理了一下,方便阅读学习 新博客[wossoneri.com] 什么是GCD GCD(Grand Central Dispatch)是li ...
- IOS学习笔记48--一些常见的IOS知识点+面试题
IOS学习笔记48--一些常见的IOS知识点+面试题 1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...
随机推荐
- [Linux] VIM 常用快捷键2
如何使用MacVim 1.在插入模式之外 基本上来说,你应该尽可能少的呆在插入模式里面,因为在插入模式里面 VIM 就像一个“哑巴”编辑器一样.很多新手都会一直呆在插入模式里面,因为这样易于使用.但 ...
- 关于NSLog
#ifdef __OBJC__#ifdef DEBUG#define NSLog(fmt,...) NSlog((@"%s [Line %d]" fmt),__PRETTY_FUN ...
- zoj 3673 1729
1729 Time Limit: 3 Seconds Memory Limit: 65536 KB 1729 is the natural number following 1728 and ...
- python之rabbitMQ篇
一.RabbitMQ安装 RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统,它遵循Mozilla Pulic License开源协议. MQ全称为Message Queue,消息队列 ...
- python学习笔记六 面向对象相关下(基础篇)
面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以将多函数中公用的变量封装到对象中) 对象,根据模板创建的 ...
- [问题2015S03] 复旦高等代数 II(14级)每周一题(第四教学周)
[问题2015S03] 设 \(g(x)=x^n+a_1x^{n-1}+\cdots+a_{n-1}x+a_n\) 是数域 \(\mathbb{K}\) 上的多项式, \(V\) 是 \(\math ...
- mysql 查询日志
1. 登录mysql mysql -u root -p; 2. 查看日志启用情况以及日志所在位置 show variables like 'log_%'; 结果示例如下 3. 找到对应的日志文件,保存 ...
- Xcode代码提示联想功能失效,按command键点不进去类库,提示“?”
参考文档:这两篇文章很好的解决了问题.可以很好的解决了问题 Xcode代码提示联想功能失效,按command键点不进去类库,提示“?”,代码全是白色 Xcode4中代码补全(Code Completi ...
- 如何修改mysql用户名和密码
如何修改mysql用户名和密码 以修改mysql的root密码为例修改的三种方法 方法1: 用SET PASSWORD命令 mysql>SET PASSWORD FOR 'root'@'lo ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:实现基本的CRUD功能
英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC 5 系列 ...