码迷,mamicode.com
首页 > 移动开发 > 详细

iOS多线程--下(GCD)

时间:2016-07-13 17:32:21      阅读:236      评论:0      收藏:0      [点我收藏+]

标签:

1 GCD

它是一种纯C语言,它是为多核并列运算设计的。可以自动管理线程的生命周期。
GCD 是面向任务和队列的,不是面向线程的。他有两个关键字“任务”“队列”。
使用 GCD 的步骤主要是:
1 定制任务
2 任务添加到队列中,队列支持 FIFO 原则

#基本形式如下
dispath_queue_t queue = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFULT,0);
dispath_async(queue,^{
    //任务代码段
});

1.1 任务

1.1.1 同步方式

在当前线程中执行,不具备开启线程的能力。

dispath_sync( queue,block); //在队列中执行 block 所定义的任务,以同步的方式

1.1.2 异步方式

在新线程中执行,具备开启新线程的能力

dispath_async(queue,block); //在队列中执行 block 所定义的任务,以异步的方式

决定了是否有能力开启新的线程

1.2 队列

1.2.1 并行队列

并行队列,队列中的任务,以并行的方式进行,多任务同时进行。
一般情况下,我们使用的是全局的并发队列。获取全局并发队列的方式如下:

//默认的写法,获取全局并发队列。前一个参数是优先级默认,后面一个参数是苹果保留的参数
dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

1.2.2 串行队列

串行队列中,队列中的人物,都是以串行的方式进行。先执行一个任务,再执行另一个任务。
串行队列分为两种,一种是手动创建的串行队列,另一个是主队列

1.2.2.1 手动创建队列
//默认写法,第一个参数是队列名称,随便填写;第二个参数是队列属性,一般情况下写 NULL
dispatch_queue_create("queueName",NULL);
1.2.2.2 主队列
//默认写法
dispath_get_main_queue();
主队列的任务执行,都是在主线程中进行的,一般用来做线程间的通信。

2 常见的组合方式

2.1 异步方式+并发队列(最常用)

①会创建新的线程
②并发执行任务
下面的例子中,任务1和任务2是并发执行的

//实例代码
//①获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
//②异步方式处理任务1
dispatch_async(queue,^{
    NSLog("下载图片1--%@",[NSThread currentThread]);
});
//②异步方式处理任务2
dispatch_async(queue,^{
    NSLog("下载图片2--%@",[NSThread currentThread]);
});

2.2 异步方式+串行队列

①会创建新线程,因为是串行方式执行,一般情况下只会新建一条线程。
②队列中的任务以串行方式执行:任务1和任务2是串行执行的

//示例代码
//①手动创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queueName",NULL);
//②异步方式处理任务1
dispatch_async(queue,^{
    NSLog("正在下载图片1---%@",[NSThread currentThread]);
});
//②异步方式处理任务2
dispatch_async(queue,^{
    NSLog("正在下载图片2---%@",[NSThread currentThread]);
});

2.3 同步方式+并发队列

①不会创建线程
②并发队列的并发功能消失,队列中的所有任务串行执行。

//示例代码
//①获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
//②同步执行任务1
dispatch_sync(queue,^{
    NSLog(@"下载图片1---%@",[NSThread currentThread]);
});
//②同步执行任务2
dispatch_sync(queue,^{
    NSLog(@"下载图片2---%@",[NSThread currentThread]);
});

2.4 同步方式+串行队列

①不会创建新线程
②串行队列中的所有任务串行执行

//示例代码
//①手动创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queueName",NULL);
//②同步执行任务1
dispatch_sync(queue,^{
    NSLog(@"正在下载图片1-----%@",[]NSThread currentThread]);
});
//②同步执行任务2
dispatch_sync(queue,^{
    NSLog(@"正在下载图片2-----%@",[]NSThread currentThread]);
});

2.5 主队列+异步方式(主队列也是串行队列)

①主队列是特殊的队列,此时异步方式虽然具备创建新线程的能力,但是实际上并不能创建新线程
②所有的任务都是在主队列中串行执行的,也就是在主线程中进行,一般用来做进程之间的通信。

//①获取当前的主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//②异步方式执行
dispatch_async(queue,^{
    NSLog(@"正在下载图片---%@",[NSThread currentThread]);
});

2.6 主队列+同步方式(主队列也是串行队列)

  • 这种方式,会卡死整个程序,不用
  • 当前执行下面的程序是在主线程中执行的,当执行到第二步的时候,需要将下面的任务加入到主队列中串行执行。因为是串行执行,主线程必须等到第二步结束后才能继续向下执行,但是第二步是将任务加载到了主队列的末尾,必须要等到之前主线程队列执行完毕后才能执行,所以陷入了卡死状态。

    //1获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    //2同步执行主队列
    dispatch_sync(queue,^{
        NSLog(@"正在执行。。。");
    });
    

3 队列的内存管理

需要遵循的原则:
* 凡是函数名中带有 create\copy\new\retain 等字眼,都应该在不需要使用这个数据的时候release 操作
* GCD的数据类型,在ARC下不需要再 release
* CF(Core Foundation)的数据类型在 ARC 和 MRC 环境下都需要手动 release。
例子:
* CFRelease(id);

NSDictionary *dict = @{@"1":@"1"};
CFDictionaryRef dictCF = (__bridge CFDictionaryRef)(dict);
CFRelease(dictCF);

4 线程之间的通信

举例,点击控制器的 View,在子线程中从网上下载一张图片,下载完毕后在主线程中更新按钮的图片。

//1 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//2 异步方式下载图片
dispatch_async(queue, ^{

    NSString *str = @"http://u1.img.mobile.sina.cn/public/files/image/600x150_img577f313ec621a.png";
    NSURL *url = [NSURL URLWithString:str];
    NSData  *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];

    //3 回到主线程,给 UIButton 设置图片
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.button setImage:image forState:UIControlStateNormal];

    });
});

5 延时执行操作

在程序设计中,延时执行的方式有三种

5.1 sleep

想要做延时操作,此种方式只需要在延时执行的操作之前执行如下代码即可

//使当前线程睡眠3秒钟后再次执行
[NSThread sleepForTimeInterval:3]

缺点:会卡死当前调用的线程,整个线程会停滞。如果卡死的是主线程,那么意味着 UI 会受很大的影响。

5.2 performSelectorAfter

//当3秒后,执行 download 方法。3秒后在主线程中执行该操作
[self performSelector:@selector(download) withObject:nil afterDelay:3]

5.3 GCD

可以根据队列的类型,决定延时后的操作在哪个线程中执行,可以指定在全局并发队列中执行,也可以指定在主队列中执行。

//dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{

    NSLog(@"延时操作---%@",[NSThread currentThread]);
});

6 一次性代码

某个代码段,在程序运行过程中,不管调用多少次,实际上改代码只执行了一次,这就是一次性代码
比如说:点击界面后,开始下载图片。如果下载图片的代码不是一次性代码的话,每点击一次界面,都需要再次下载,显然不符合实际情况。使用其他方式也可以实现(定义变量或者定义标识),但一次性代码是最简单的操作。

static dispatch_once_t oneceToken;
dispatch_once(&onceToken,^{
    //想要只执行一次的代码
    //.....
});

7 队列组

队列组就是一个对象,内部包含了队列,当队列中的任务执行完毕后,会自动调用响应的方法,这就是队列组。

需求:
需要从网络上下载一张图片,然后再下载一张 logo 图片,用 logo 做水印。如果按照顺序,先下载图片,再下载 logo,最后进行图片水印叠加,会比较耗时。最好的办法是 两张图片分别放到两个线程中进行,当两个图片全部下载好之后,再进行组装。               
实现思路:
采用队列组,手动创建一个队列组,使用dispatch_group_async(group,queue,^{})来开辟新线程执行下载操作。当队列组中的队列任务全部结束之后,会自动调用 dispatch_group_notify(queue,^{})函数,也就是说我们可以将合并水印的操作放在这个函数中进行。
示例代码
//1 获取全局并发队列
dispatch_queue_t queue  = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY——DEFAULT,0);

//2 创建队列组--如果是 MRC 环境中,需要 release操作: dispatch_release(group);
dispatch_group_t group = dispatch_group_create(); 

//3 下载图片1
__block UIImage *image1 = nil; //加__block 修饰是为了 block 中可以访问该变量
dispatch_group_async(group,queue,^{
    NSString *str = @"http://image1.png";
    NSURL *url = [NSURL urlWithString:str];
    NSData *data = [NSData dataWithContentsOfURL:url];
    image1 = [UIImage imageWithData:data];
});

//4 下载图片2
__block UIImage *image2 = nil; //加__block 修饰是为了 block 中可以访问该变量
dispatch_group_async(group,queue,^{
    NSString *str = @"http://image2.png";
    NSURL *url = [NSURL urlWithString:str];
    NSData *data = [NSData dataWithContentsOfURL:url];
    image2 = [UIImage imageWithData:data];
});

//5 合并图片--group中所有队列的任务执行完之后,自动调用下面的函数
dispatch_group_notify(group,queue,^{
    //5.1 开启当前图形上下文
    UIGraphicsBeginImageContextWithOptions(image1.size,NO,0.0);
    //5.2 将 image1画在上下文
    [image1 drawInRect:CGRectMake(0,0,image1.size.width,image1..size.height)];
    //5.3 将 image2画在上下文
    [image2 drawInRect:CGRectMake(0,0,100.50)];
    //5.4 获取当前上下文的图片
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    //5.6 回到主线程,刷新 UI
    dispatch_async(dispath_get_main_queue,^{
        self.imageView.image = image;
    });
});

iOS多线程--下(GCD)

标签:

原文地址:http://blog.csdn.net/smallyou113/article/details/51882683

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!