iOS学习之GCD

多线程编程

线程定义:一个CPU执行的CPU命令 列一条无分叉的路径就叫线程

多线程:执行多个不同的CPU命令 有多条路径

线程的使用:主线程(又叫作UI线程)主要任务是处理UI事件,显示和刷新UI,(只有主线程有直接修改UI的能力)耗时的操作放在子线程(又叫作后台线程、异步线程)。

多线程容易引发的编程问题:

  1. 数据竞争:多个线程更新相同的资源会导致数据不一致
  2. 死锁:停止等待事件的线程会导致多个线程相互持续等待
  3. 太多线程会消耗大量内存
  4. Thread Safe(线程安全):一段线程安全的代码(对象),可以同时被多个线程或并发的任务调度,不会产生问题,非线程安全的只能按次序被访问。

多线程优点:在需要常实际处理方法放到其他线程中,让其他线程处理,主线程不处理

GCD之前的多线程方法:NSThread, NSOperationQueue, NSInvocationOperation

GCD:Grand Central Dispatch:异步执行任务的技术。

没有使用GCD时,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是在使用GCD后,app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。

方法优缺点分析:

  1. NSThread (抽象层次:低)
  • 优点:轻量级,简单易用,可以直接操作线程对象
  • 缺点: 需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销。
  1. Cocoa NSOperation(抽象层次:中)
  • 优点:不需要关心线程管理,数据同步的事情,可以把精力放在学要执行的操作上。基于GCD,是对GCD 的封装,比GCD更加面向对象
  • 缺点: NSOperation是个抽象类,使用它必须使用它的子类,可以实现它或者使用它定义好的两个子类NSInvocationOperation、NSBlockOperation。
  1. GCD抽象层次:高)
  • 优点:是 Apple 开发的一个多核编程的解决方法,简单易用,效率高,速度快,基于C语言,更底层更高效,并且不是Cocoa框架的一部分,自动管理线程生命周期(创建线程、调度任务、销毁线程)。
  • 缺点: 使用GCD的场景如果很复杂,就有非常大的可能遇到死锁问题。

GCD的优势

  1. GCD是苹果公司为多核的并行运算提出的解决方案
  2. GCD会自动利用更多的CPU内核(比如双核、四核)
  3. GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  4. 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

在GCD之前CoCoa框架提供了NSObject类的 performSelectorInBackgroud:withObject实例方法和performSelectorOnMainThread实例方法。

  • PerformSelector在内存管理方面会很容易有疏失,它无法确定将要执行的selector具体是什么,所以ARC没办法插入适当的内存管理方法

SEL selector;

If(    ){

[email protected] selector(newObject)}

Else if(){
[email protected] selector(copy)}

Else {
[email protected] selector(somesasr)}

Id ret=[object performSelector:selector // 这样编译器不知道将要执行的seletor是哪个,必须到运行期才能确定,ARC没办法插入适当的内存管理方法。会出现错误警告。

];

//前两个需要自己释放,后面一个不需要自己释放,使用ARC会出现错误警告,不使用ARC很容易忽视掉,就算使用静态分析其,也难侦测到

  • 处理的东西太过于局限,返回值类型及发送给方法的参数个数都受到限制。
  • 如果想要把任务放在另一个线程上执行,最好不要用performSelector方法,应该封装到block中,然后调用GCD来实现。

performSelector跟GCD处理同一个任务:

1)[self performSelector:@selector(dosomething)

withObject:nil

after:5.0];// performSelector

2)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方法:

  1. 后台执行:

dispatch_async(dispatch_get_global_queue(0,0),^{

//something

});

  1. 主线程执行:

dispatch_async(dispatch_get_main_queue(),^{

//something

});

  1. 一次性执行:dispatch_once保证在app运行期间,block中的代码只执行一次
  • 经典使用场景---单例
  • 单例对象ShareManager的定义:

Static dispatch_once_t onceToken;

dispatch_once(&onceToken ,^{

//something

});

  1. 延迟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

});

  1. 自定义执行:

要自定义queue可以用dispatch_queue_create

祥解GCD 的API

说明:只要将想执行的任务加入到适当的Dispatch Queue中。

两种Dispatch Queue:(Concurrent Dispatch Queue)

   

  1. Serial Dispatch Queue串行队列(图1): 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

Serial Dispatch Queue太多容易消耗太多内存。

所以只在可能数据竞争时使用Serial Dispatch Queue。

  1. Concurrent Dispatch Queue并发队列(图2):不等待现在执行中处理结束(可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效)一个线程可同时处理多个任务。在不发生数据竞争时都可用。
    1. 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释放。

    1. Main Dispatch Queue/ Global Dispatch Queue
  1. Main Dispatch Queue属于Serial Dispatch Queue 类型。
  2. Global Dispatch Queue属于 Concurrent Dispatch Queue类型,有四种优先级:High priority(高优先级)、Default priority(默认优先级),Low priority(低优先级),Background priority(后台优先级)
    1. Dispatch_after
  • 功能:让任务延时添加到队列,将一个Block在特定的延时以后,加入到指定的队列中,不是在特定的时间后立即运行
  • 使用方法:
    • dispatch_time_t delayTime3 = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
    • dispatch_time_t delayTime2 = dispatch_time(DISPATCH_TIME_NOW, 2*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");

输出结果:

    • 2015-11-19 15:50:19.369 Whisper[2725:172593] current task
    • 2015-11-19 15:50:19.370 Whisper[2725:172593] next task
    • 2015-11-19 15:50:21.369 Whisper[2725:172593] 2秒之后添加到队列
    • 2015-11-19 15:50:29.654 Whisper[2725:172593] 3秒之后添加到队列
  • 注意点:dispatch_after只是延时提交block,并不是延时后立即执行
    1. 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, 1 * NSEC_PER_SEC);

dispatch_time(DISPATCH_TIME_NOW, 1000 * 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,每毫秒有多少纳秒。
    1. Dispatch Group:dispatch_group_async
  • 功能:当遇到需要执行多个线程并发执行,然后等多个线程都结束之后,再汇总执行结果时可以用group queue(同时下载多个图片,所有图片下载完成之后去更新UI(需要回到主线程)或者去处理其他任务(可以是其他线程队列)。)
  • 使用方法:
    • dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    • dispatch_group_t group = dispatch_group_create();
    • dispatch_group_async(group, queue, ^{
    • [NSThread sleepForTimeInterval:1];
    • NSLog(@"picture1 download complete ");
    • });
    • dispatch_group_async(group, queue, ^{
    • [NSThread sleepForTimeInterval:2];
    • NSLog(@"picture2 download complete ");
    • });
    • dispatch_group_async(group, queue, ^{
    • [NSThread sleepForTimeInterval:3];
    • 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:04:16.737 gcdTest[43328:11303] picture1 download complete
16:04:17.738 gcdTest[43328:12a1b] picture2 download complete
16:04:18.738 gcdTest[43328:13003] picture3 download complete
16:04:18.739 gcdTest[43328:f803] update Ui

  • 注意点:
    1. Dispatch_barrier_async: 
  • 功能:在它前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
  • 使用方法:
    1. dispatch_async(queue, ^{
    2. [NSThread sleepForTimeInterval:2];
    3. NSLog(@"dispatch_async1");
    4. });  //第一个队列
    5. dispatch_async(queue, ^{
    6. [NSThread sleepForTimeInterval:4];
    7. NSLog(@"dispatch_async2");
    8. });  第二个队列
    9. dispatch_barrier_async(queue, ^{
    10. NSLog(@"dispatch_barrier_async");
    11. [NSThread sleepForTimeInterval:4];
    12. });  // barrier队列
    13. dispatch_async(queue, ^{
    14. [NSThread sleepForTimeInterval:1];
    15. NSLog(@"dispatch_async3");
    16. });  // 第四个队列

运行思路:第一二个block同时运行,等一二block都运行完开始运行barrier队列,barrier队列运行完后,运行第四个block

运行结果:16:20:31开始运行

 16:20:33.967 gcdTest[45547:11203] dispatch_async1

16:20:35.967 gcdTest[45547:11303] dispatch_async2

16:20:35.967 gcdTest[45547:11303] dispatch_barrier_async

16:20:40.970 gcdTest[45547:11303] dispatch_async3

  • 注意点:
    1. Dispatch_sync
  • 功能: 
  • 使用方法:
  • 注意点:
    1. Dispatch_apply
  • 功能:把一项任务提交到队列中多次执行,队列可以是串行也可以是并行,dispatch_apply不会立刻返回,而是在执行完block中的任务后才会返回,是同步执行的函数。
  • 使用方法:
    1. dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    2. NSLog(@"current task");
    3. dispatch_async(globalQueue, ^{
    4. dispatch_queue_t applyQueue = dispatch_get_global_queue(0, 0);
    5. //第一个参数,3--block执行的次数
    6. //第二个参数,applyQueue--block任务提交到的队列
    7. //第三个参数,block--需要重复执行的任务
    8. dispatch_apply(3, applyQueue, ^(size_t index) {
    9. NSLog(@"current index %@",@(index));
    10. sleep(1);
    11. });
    12. NSLog(@"dispatch_apply 执行完成");
    13. dispatch_queue_t mainQueue = dispatch_get_main_queue();
    14. dispatch_async(mainQueue, ^{
    15. NSLog(@"回到主线程更新UI");});
    16. });
    17. NSLog(@"next task");
  • 注意点:在主线程直接调用dispatch_apply会阻塞主线程,为了不阻塞主线程,一般把dispatch_apply放在异步队列中调用,然后执行完成后通知主线程
    1. Dispatch_suspend/dispatch_resume
  • 功能:提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的block

使用方法:

    1. dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
    2. //提交第一个block,延时5秒打印
    3. dispatch_async(queue, ^{
    4. [NSThread sleepForTimeInterval:5];
    5. NSLog(@"Block2 printf after 5 seconds...");});
    6. //提交第二个block,延时15秒打印
    7. dispatch_async(queue, ^{
    8. [NSThread sleepForTimeInterval:15];
    9. NSLog(@"Block1 printf after 15 seconds ...");
    10. });
    11. //延时一秒
    12. NSLog(@"sleep 1 second...");
    13. [NSThread sleepForTimeInterval:1];
  1. //挂起队列
  2. dispatch_suspend(queue);
  3. //延时10秒
  4. NSLog(@"sleep 10 second...");
  5. [NSThread sleepForTimeInterval:10];
  6. //恢复队列
  7. NSLog(@"resume...");
  8. 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还是在运行,并且正常输出。

运行结果:

    1. 00:32:09.903   sleep 1 second...
    2. 00:32:10.910  suspend...
    3. 00:32:10.910  sleep 10 second...
    4. 00:32:14.908  Block1 printf after 5 seconds...(block1持续运行)
    5. 00:32:20.911  resume...
    6. 00:32:35.912  Block2 printf after 15 seconds ...

注意点:dispatch_suspend并不会立即暂停正在运行的block,而是在当前block执行完成后,暂停后续的block执行。所以想暂停正在队列上运行的block时,还是不要用dispatch_suspend

    1. Dispatch_once
  • 功能:被广泛使用在单例、缓存等代码中,用以保证在初始化时执行一次某任务。在单线程程序中毫无意义,但在多线程程序中,其低负载、高可依赖性、接口简单等特性
  • 使用方法:
  1. static dispatch_once_t once;
  2. dispatch_once(&once, ^{
  3. //单例代码
  4. });
  • 注意点: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;

}

时间: 2024-10-27 05:42:58

iOS学习之GCD的相关文章

IOS学习之十七:Grand Central Dispatch(GCD)编程基础

IOS学习之十七:Grand Central Dispatch(GCD)编程基础 有过编程经验的人,基本都会接触到多线程这块. 在java中以及Android开发中,大量的后台运行,异步消息队列,基本都是运用了多线程来实现. 同样在,在ios移动开发和Android基本是很类似的一种模型. 但是很多时候,在应用开发中,我们会发现本身并没有自己编码去处理一些并发的事件,去开辟新的子线程等等. (虽然一般的调用sdk发起一个网络请求,系统都是会默认给你新起一个线程去处理的). 整个程序看上去基本就是

iOS学习——并发编程GCD

在iOS中使用的多线程技术有四种,Pthread.NSThread.GCD.NSOperation,但GCD与OP严格来说,应该叫并发编程技术.GCD虽然是用C语言书写,但是苹果对它做了很多封装,让它使用起来及其简单方便,因此在OC开发中应用很广.而OP则是在iOS4.0之后对GCD进行了一次封装,增加了许多用GCD实现比较麻烦的功能,如最大并发数,线程暂停,取消以及依赖添加. GCD的使用其实可以拆分成两步,一是队列,二是任务/指令:队列分为串行队列.并发队列.全局队列以及主队列,其中主队列只

2015最新iOS学习线路图

iOS是由苹果公司开发的移动操作系统,以xcode为主要开发工具,具有简单易用的界面.令人惊叹的功能,以及超强的稳定性,已经成为iPhone.iPad 和iPod touch 的强大基础:iOS 内置的众多技术和功能让 Apple设备始终保持着遥遥领先的地位. iOS学习路线:http://www.mobiletrain.org/page/ios.html 课程分  类 课程模块 模块介绍 课程内容 Part1C语言 C语言和Objective-C语言 C语言 Mac系统及常用工具.进制:C数据

ios学习记录 day41 UI17 多线程

CPU(工厂) 进程(车间) 线程(工人) 一个进程代表一个应用程序 CPU总是运行一个进程,其它进程处于非运行状态.一个进程可以包含多个线程.线程与线程之间可以共享进程的内存区域. 打开一个应用程序,系统会给我们创建一个线程,称为主线程 管理主界面的UI与内部循环机制(与界面相关的东西必须放在主线程中!!!) 压力比较大且会造成线程阻塞(界面卡),因此我们通过创建子线程来对主线程进行分压. 什么时候用多线程 1.网络请求(同步的) 2.文件读写(少) 3.大数据计算(冒泡) 4.数据库sele

iOS学习笔记-精华整理

iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始等待用户的操作,自动释放池就会被释放掉(调用dealloc),池中的对象都会收到一个release,有可能会因此被销毁. 2-成员属性:     readonly:不指定readonly,默认合成getter和setter方法.外界毫不关心的成员,则不要设置任何属性,这样封装能增加代码的独立性和安全

IOS学习笔记 -- 多线程

多线程1.多线程的原理 1>.同一时间,CPU只能处理1条线程,只有1条线程在工作(执行) 2>.多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换) 3>.如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象如果线程非常非常多,会发生: 1>.CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源 2>.每条线程被调度执行的频次会降低(线程的执行效率降低) 2.多线程的优缺点 1>.多线程的优点 能适当提高程序的执行效率 能适当提高资源

iOS学习资源收集

https://github.com/Tim9Liu9/TimLiu-iOS 自己总结的iOS.mac开源项目及库,持续更新.... github排名 https://github.com/trending,github搜索:https://github.com/search 目录 UI 下拉刷新 模糊效果 AutoLayout 富文本 图表 表相关与Tabbar 隐藏与显示 HUD与Toast 对话框 其他UI 动画 侧滑与右滑返回手势 gif动画 其他动画 网络相关 网络连接 图像获取 网络

iphone ios 如何使用gcd,block

iphone ios 如何使用gcd,block 转自:http://blog.sina.com.cn/s/blog_45e2b66c01010dhd.html 1.GCD之dispatch queue http://www.cnblogs.com/scorpiozj/archive/2011/07/25/2116459.html 2.iOS中GCD的魔力 http://blog.csdn.net/favormm/article/details/6453260 3.官方 ,内容真的很多 http

IOS学习之UiTableView下拉刷新与自动加载更多,百年不变的效果

IOS学习之UiTableView下拉刷新与自动加载更多,百年不变的效果(五) 五一劳动节马上来临,小伙伴有妹有很激动哟,首先祝天下所有的程序猿节日快乐!这个五一对于我来说有点不一样,我的人生从这个五一就转弯了,爱情长跑8年的我结婚了,一会支付宝账号我会公布出去,请自觉打款!谢谢合作. 灯光闪起来: 舞蹈跳起来: 歌曲唱起来: -------------------------------------------------------------------------------------