-
iOS开发多线程篇—GCD的常见用法
一、延迟执行 1.介绍 iOS常见的延时执行有2种方式(1)调用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后再调用self的run方法
(2)使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
});
2.说明
第一种方法,该方法在那个线程调用,那么run就在哪个线程执行(当前线程),通常是主线程。[self performSelector:@selector(run) withObject:nil afterDelay:3.0];
说明:在3秒钟之后,执行run函数
代码示例:
01.
1
//
02.
2
// YYViewController.m
03.
3
// 01-GCD的常见使用(延迟执行)
04.
4
//
05.
5
// Created by apple on 14-6-25.
06.
6
// Copyright (c) 2014年 itcase. All rights reserved.
07.
7
//
08.
8
09.
9
#
import
"YYViewController.h"
10.
10
11.
11
@interface
YYViewController ()
12.
12
13.
13
@end
14.
14
15.
15
@implementation
YYViewController
16.
16
17.
17
- (
void
)viewDidLoad
18.
18
{
19.
19
[
super
viewDidLoad];
20.
20
NSLog(@
"打印线程----%@"
,[NSThread currentThread]);
21.
21
//延迟执行
22.
22
//第一种方法:延迟3秒钟调用run函数
23.
23
[self performSelector:
@selector
(run) withObject:nil afterDelay:
2.0
];
24.
24
25.
25
}
26.
26
-(
void
)run
27.
27
{
28.
28
NSLog(@
"延迟执行----%@"
,[NSThread currentThread]);
29.
29
}
30.
30
31.
31
-(
void
)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
32.
32
{
33.
33
//在异步函数中执行
34.
34
dispatch_queue_t queue = dispatch_queue_create(
"wendingding"
,
0
);
35.
35
36.
36
dispatch_sync(queue, ^{
37.
37
[self performSelector:
@selector
(test) withObject:nil afterDelay:
1.0
];
38.
38
});
39.
39
NSLog(@
"异步函数"
);
40.
40
}
41.
41
-(
void
)test
42.
42
{
43.
43
NSLog(@
"异步函数中延迟执行----%@"
,[NSThread currentThread]);
44.
44
}
45.
45
@end
说明:如果把该方法放在异步函数中执行,则方法不会被调用(BUG?)
第二种方法,dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//延迟执行的方法
});
说明:在5秒钟之后,执行block中的代码段。
参数说明:
什么时间,执行这个队列中的这个任务。
代码示例:
01.
1
//
02.
2
// YYViewController.m
03.
3
// 02-GCD常见使用(延迟执行2)
04.
4
//
05.
5
// Created by apple on 14-6-25.
06.
6
// Copyright (c) 2014年 itcase. All rights reserved.
07.
7
//
08.
8
09.
9
#
import
"YYViewController.h"
10.
10
11.
11
@interface
YYViewController ()
12.
12
13.
13
@end
14.
14
15.
15
@implementation
YYViewController
16.
16
17.
17
- (
void
)viewDidLoad
18.
18
{
19.
19
[
super
viewDidLoad];
20.
20
21.
21
NSLog(@
"打印当前线程---%@"
, [NSThread currentThread]);
22.
22
23.
23
//延迟执行,第二种方式
24.
24
//可以安排其线程(1),主队列
25.
25
dispatch_queue_t queue= dispatch_get_main_queue();
26.
26
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(
5.0
* NSEC_PER_SEC)), queue, ^{
27.
27
NSLog(@
"主队列--延迟执行------%@"
,[NSThread currentThread]);
28.
28
});
29.
29
30.
30
//可以安排其线程(2),并发队列
31.
31
//1.获取全局并发队列
32.
32
dispatch_queue_t queue1= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0
);
33.
33
//2.计算任务执行的时间
34.
34
dispatch_time_t when=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(
5.0
* NSEC_PER_SEC));
35.
35
//3.会在when这个时间点,执行queue中的这个任务
36.
36
dispatch_after(when, queue1, ^{
37.
37
NSLog(@
"并发队列-延迟执行------%@"
,[NSThread currentThread]);
38.
38
});
39.
39
}
40.
40
41.
41
@end
延迟执行:不需要再写方法,且它还传递了一个队列,我们可以指定并安排其线程。
如果队列是主队列,那么就在主线程执行,如果队列是并发队列,那么会新开启一个线程,在子线程中执行。
二、一次性代码
1.实现一次性代码
需求:点击控制器只有第一次点击的时候才打印。
实现代码:
01.
1
//
02.
2
// YYViewController.m
03.
3
// 03-GCD常见使用(一次性代码)
04.
4
//
05.
5
// Created by apple on 14-6-25.
06.
6
// Copyright (c) 2014年 itcase. All rights reserved.
07.
7
//
08.
8
09.
9
#
import
"YYViewController.h"
10.
10
11.
11
@interface
YYViewController ()
12.
12
@property
(nonatomic,assign) BOOL log;
13.
13
@end
14.
14
15.
15
@implementation
YYViewController
16.
16
17.
17
-(
void
)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
18.
18
{
19.
19
if
(_log==NO) {
20.
20
NSLog(@
"该行代码只执行一次"
);
21.
21
_log=YES;
22.
22
}
23.
23
}
24.
24
@end
缺点:这是一个对象方法,如果又创建一个新的控制器,那么打印代码又会执行,因为每个新创建的控制器都有自己的布尔类型,且新创建的默认为NO,因此不能保证改行代码在整个程序中只打印一次。
2.使用dispatch_once一次性代码
使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
整个程序运行过程中,只会执行一次。
代码示例:
01.
1
//
02.
2
// YYViewController.m
03.
3
// 03-GCD常见使用(一次性代码)
04.
4
//
05.
5
// Created by apple on 14-6-25.
06.
6
// Copyright (c) 2014年 itcase. All rights reserved.
07.
7
//
08.
8
09.
9
#
import
"YYViewController.h"
10.
10
11.
11
@interface
YYViewController ()
12.
12
@property
(nonatomic,assign) BOOL log;
13.
13
@end
14.
14
15.
15
@implementation
YYViewController
16.
16
17.
17
//-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
18.
18
//{
19.
19
// if (_log==NO) {
20.
20
// NSLog(@"该行代码只执行一次");
21.
21
// _log=YES;
22.
22
// }
23.
23
//}
24.
24
25.
25
-(
void
)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
26.
26
{
27.
27
static
dispatch_once_t onceToken;
28.
28
dispatch_once(&onceToken, ^{
29.
29
NSLog(@
"该行代码只执行一次"
);
30.
30
});
31.
31
}
32.
32
@end
效果(程序运行过程中,打印代码只会执行一次):
三、队列组
需求:从网络上下载两张图片,把两张图片合并成一张最终显示在view上。
1.第一种方法
代码示例:
01.
1
//
02.
2
// YYViewController.m
03.
3
// 04-GCD基本使用(队列组下载图片)
04.
4
//
05.
5
// Created by apple on 14-6-25.
06.
6
// Copyright (c) 2014年 itcase. All rights reserved.
07.
7
//
08.
8
09.
9
#
import
"YYViewController.h"
10.
10
//宏定义全局并发队列
11.
11
#define global_quque dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0
)
12.
12
//宏定义主队列
13.
13
#define main_queue dispatch_get_main_queue()
14.
14
15.
15
@interface
YYViewController ()
16.
16
@property
(weak, nonatomic) IBOutlet UIImageView *imageView1;
17.
17
@property
(weak, nonatomic) IBOutlet UIImageView *imageView2;
18.
18
@property
(weak, nonatomic) IBOutlet UIImageView *imageView3;
19.
19
20.
20
@end
21.
21
22.
22
@implementation
YYViewController
23.
23
24.
24
- (
void
)viewDidLoad
25.
25
{
26.
26
[
super
viewDidLoad];
27.
27
}
28.
28
-(
void
)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
29.
29
{
30.
30
//获取全局并发队列
31.
31
// dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
32.
32
//获取主队列
33.
33
// dispatch_queue_t queue= dispatch_get_main_queue();
34.
34
37.
37
dispatch_async(global_quque, ^{
38.
38
//下载图片1
39.
39
UIImage *image1= [self imageWithUrl:@
"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"
];
40.
40
NSLog(@
"图片1下载完成---%@"
,[NSThread currentThread]);
41.
41
42.
42
//下载图片2
43.
43
UIImage *image2= [self imageWithUrl:@
"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"
];
44.
44
NSLog(@
"图片2下载完成---%@"
,[NSThread currentThread]);
45.
45
46.
46
//回到主线程显示图片
47.
47
dispatch_async(main_queue, ^{
48.
48
NSLog(@
"显示图片---%@"
,[NSThread currentThread]);
49.
49
self.imageView1.image=image1;
50.
50
self.imageView2.image=image2;
51.
51
//合并两张图片
52.
52
UIGraphicsBeginImageContextWithOptions(CGSizeMake(
200
,
100
), NO,
0.0
);
53.
53
[image1 drawInRect:CGRectMake(
0
,
0
,
100
,
100
)];
54.
54
[image2 drawInRect:CGRectMake(
100
,
0
,
100
,
100
)];
55.
55
self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
56.
56
//关闭上下文
57.
57
UIGraphicsEndImageContext();
58.
58
NSLog(@
"图片合并完成---%@"
,[NSThread currentThread]);
59.
59
});
60.
60
//
61.
61
});
62.
62
}
63.
63
64.
64
//封装一个方法,传入一个url参数,返回一张网络上下载的图片
65.
65
-(UIImage *)imageWithUrl:(NSString *)urlStr
66.
66
{
67.
67
NSURL *url=[NSURL URLWithString:urlStr];
68.
68
NSData *data=[NSData dataWithContentsOfURL:url];
69.
69
UIImage *image=[UIImage imageWithData:data];
70.
70
return
image;
71.
71
}
72.
72
@end
显示效果:
打印查看:
问题:这种方式的效率不高,需要等到图片1.图片2都下载完成后才行。
提示:使用队列组可以让图片1和图片2的下载任务同时进行,且当两个下载任务都完成的时候回到主线程进行显示。
2.使用队列组解决
步骤:
创建一个组
开启一个任务下载图片1
开启一个任务下载图片2
同时执行下载图片1下载图片2操作
等group中的所有任务都执行完毕, 再回到主线程执行其他操作
代码示例
001.
1
//
002.
2
// YYViewController.m
003.
3
// 04-GCD基本使用(队列组下载图片)
004.
4
//
005.
5
// Created by apple on 14-6-25.
006.
6
// Copyright (c) 2014年 itcase. All rights reserved.
007.
7
//
008.
8
009.
9
#
import
"YYViewController.h"
010.
10
//宏定义全局并发队列
011.
11
#define global_quque dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0
)
012.
12
//宏定义主队列
013.
13
#define main_queue dispatch_get_main_queue()
014.
14
015.
15
@interface
YYViewController ()
016.
16
@property
(weak, nonatomic) IBOutlet UIImageView *imageView1;
017.
17
@property
(weak, nonatomic) IBOutlet UIImageView *imageView2;
018.
18
@property
(weak, nonatomic) IBOutlet UIImageView *imageView3;
019.
19
020.
20
@end
021.
21
022.
22
@implementation
YYViewController
023.
23
024.
24
- (
void
)viewDidLoad
025.
25
{
026.
26
[
super
viewDidLoad];
027.
27
}
028.
28
-(
void
)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
029.
29
{
032.
32
033.
33
034.
34
//1.创建一个队列组
035.
35
dispatch_group_t group = dispatch_group_create();
036.
36
037.
37
//2.开启一个任务下载图片1
038.
38
__block UIImage *image1=nil;
039.
39
dispatch_group_async(group, global_quque, ^{
040.
40
image1= [self imageWithUrl:@
"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"
];
041.
41
NSLog(@
"图片1下载完成---%@"
,[NSThread currentThread]);
042.
42
});
043.
43
044.
44
//3.开启一个任务下载图片2
045.
45
__block UIImage *image2=nil;
046.
46
dispatch_group_async(group, global_quque, ^{
047.
47
image2= [self imageWithUrl:@
"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"
];
048.
48
NSLog(@
"图片2下载完成---%@"
,[NSThread currentThread]);
049.
49
});
050.
50
051.
51
//同时执行下载图片1下载图片2操作
052.
52
053.
53
//4.等group中的所有任务都执行完毕, 再回到主线程执行其他操作
054.
54
dispatch_group_notify(group,main_queue, ^{
055.
55
NSLog(@
"显示图片---%@"
,[NSThread currentThread]);
056.
56
self.imageView1.image=image1;
057.
57
self.imageView2.image=image2;
058.
58
059.
59
//合并两张图片
060.
60
//注意最后一个参数是浮点数(0.0),不要写成0。
061.
61
UIGraphicsBeginImageContextWithOptions(CGSizeMake(
200
,
100
), NO,
0.0
);
062.
62
[image1 drawInRect:CGRectMake(
0
,
0
,
100
,
100
)];
063.
63
[image2 drawInRect:CGRectMake(
100
,
0
,
100
,
100
)];
064.
64
self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
065.
65
//关闭上下文
066.
66
UIGraphicsEndImageContext();
067.
67
068.
68
NSLog(@
"图片合并完成---%@"
,[NSThread currentThread]);
069.
69
});
070.
70
071.
71
}
072.
72
-(
void
)download2image
073.
73
{
074.
74
//获取全局并发队列
075.
75
// dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
076.
76
//获取主队列
077.
77
// dispatch_queue_t queue= dispatch_get_main_queue();
078.
78
079.
79
dispatch_async(global_quque, ^{
080.
80
//下载图片1
081.
81
UIImage *image1= [self imageWithUrl:@
"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"
];
082.
82
NSLog(@
"图片1下载完成---%@"
,[NSThread currentThread]);
083.
83
084.
84
//下载图片2
085.
85
UIImage *image2= [self imageWithUrl:@
"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"
];
086.
86
NSLog(@
"图片2下载完成---%@"
,[NSThread currentThread]);
087.
87
088.
88
//回到主线程显示图片
089.
89
dispatch_async(main_queue, ^{
090.
90
NSLog(@
"显示图片---%@"
,[NSThread currentThread]);
091.
91
self.imageView1.image=image1;
092.
92
self.imageView2.image=image2;
093.
93
//合并两张图片
094.
94
UIGraphicsBeginImageContextWithOptions(CGSizeMake(
200
,
100
), NO,
0.0
);
095.
95
[image1 drawInRect:CGRectMake(
0
,
0
,
100
,
100
)];
096.
96
[image2 drawInRect:CGRectMake(
0
,
0
,
100
,
100
)];
097.
97
self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
098.
98
//关闭上下文
099.
99
UIGraphicsEndImageContext();
100.
100
NSLog(@
"图片合并完成---%@"
,[NSThread currentThread]);
101.
101
});
102.
102
//
103.
103
});
104.
104
}
105.
105
106.
106
//封装一个方法,传入一个url参数,返回一张网络上下载的图片
107.
107
-(UIImage *)imageWithUrl:(NSString *)urlStr
108.
108
{
109.
109
NSURL *url=[NSURL URLWithString:urlStr];
110.
110
NSData *data=[NSData dataWithContentsOfURL:url];
111.
111
UIImage *image=[UIImage imageWithData:data];
112.
112
return
image;
113.
113
}
114.
114
@end
打印查看(同时开启了两个子线程,分别下载图片):
2.补充说明
有这么1种需求:
首先:分别异步执行2个耗时的操作
其次:等2个异步操作都执行完毕后,再回到主线程执行操作
如果想要快速高效地实现上述需求,可以考虑用队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});