在iOS所有实现多线程的方案中,GCD应该是最有魅力的,因为GCD本身是苹果公司为多核的并行运算提出的解决方案。GCD在工作时会自动利用更多的处理器核心,以充分利用更强大的机器。GCD是Grand Central Dispatch的简称,它是基于C语言的。如果使用GCD,完全由系统管理线程,我们不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue)。GCD会负责创建线程和调度你的任务,系统直接提供线程管理
3、dispatch queue按先进先出的顺序,串行或并发地执行任务
// 获取默认优先级的全局并发dispatch queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
第一个参数用于指定优先级,分别使用DISPATCH_QUEUE_PRIORITY_HIGH和DISPATCH_QUEUE_PRIORITY_LOW两个常量来获取高和低优先级的两个queue;第二个参数目前未使用到,默认0即可。
dispatch_queue_t queue;
queue = dispatch_queue_create("cn.itcast.queue", NULL);
GCD提供了函数让应用访问几个公共dispatch queue:
要执行一个任务,你需要将它添加到一个适当的dispatch queue,你可以单个或按组来添加,也可以同步或异步地执行一个任务,也。一旦进入到queue,queue会负责尽快地执行你的任务。一般可以用一个block来封装任务内容。
// 调用前,查看下当前线程
NSLog(@"当前调用线程:%@", [NSThread currentThread]);
// 创建一个串行queue
dispatch_queue_t queue = dispatch_queue_create("cn.itcast.queue", NULL);
dispatch_async(queue, ^{
NSLog(@"开启了一个异步任务,当前线程:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"开启了一个同步任务,当前线程:%@", [NSThread currentThread]);
});
// 销毁队列
dispatch_release(queue);
打印信息:
2013-02-03 09:03:37.348 thread[6491:c07] 当前调用线程:<NSThread: 0x714fa80>{name = (null), num = 1}
2013-02-03 09:03:37.349 thread[6491:1e03] 开启了一个异步任务,当前线程:<NSThread: 0x74520a0>{name = (null), num = 3}
2013-02-03 09:03:37.350 thread[6491:c07] 开启了一个同步任务,当前线程:<NSThread: 0x714fa80>{name = (null), num = 1}
如果你使用循环执行固定次数的迭代, 并发dispatch queue可能会提高性能。
例如下面的for循环:
int i;
int count = 10;
for (i = 0; i < count; i++) {
printf("%d ",i);
}
// 获得全局并发queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
size_t count = 10;
dispatch_apply(count, queue, ^(size_t i) {
printf("%zd ", i);
});
// 销毁队列
dispatch_release(queue);
打印信息:
1 2 0 3 4 5 6 7 8 9
可以看出,这些迭代是并发执行的和普通for循环一样,dispatch_apply和dispatch_apply_f函数也是在所有迭代完成之后才会返回,因此这两个函数会阻塞当前线程,主线程中调用这两个函数必须小心,可能会阻止事件处理循环并无法响应用户事件。所以如果循环代码需要一定的时间执行,可以考虑在另一个线程中调用这两个函数。如果你传递的参数是串行queue,而且正是执行当前代码的queue,就会产生死锁。
// 异步下载图片
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:@"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg"];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
// 回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
GCD支持Cocoa内存管理机制,因此可以在提交到queue的block中自由地使用Objective-C对象。每个dispatch queue维护自己的autorelease pool确保释放autorelease对象,但是queue不保证这些对象实际释放的时间。如果应用消耗大量内存,并且创建大量autorelease对象,你需要创建自己的autorelease pool,用来及时地释放不再使用的对象。
我们可以使用dispatch_suspend函数暂停一个queue以阻止它执行block对象;使用dispatch_resume函数继续dispatch queue。调用dispatch_suspend会增加queue的引用计数,调用dispatch_resume则减少queue的引用计数。当引用计数大于0时,queue就保持挂起状态。因此你必须对应地调用suspend和resume函数。挂起和继续是异步的,而且只在执行block之间(比如在执行一个新的block之前或之后)生效。挂起一个queue不会导致正在执行的block停止。
假设有这样一个需求:从网络上下载两张不同的图片,然后显示到不同的UIImageView上去,一般可以这样实现
// 根据url获取UIImage
- (UIImage *)imageWithURLString:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
return [UIImage imageWithData:data];
}
- (void)downloadImages {
// 异步下载图片
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 下载第一张图片
NSString *url1 = @"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";
UIImage *image1 = [self imageWithURLString:url1];
// 下载第二张图片
NSString *url2 = @"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";
UIImage *image2 = [self imageWithURLString:url2];
// 回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView1.image = image1;
self.imageView2.image = image2;
});
});
}
虽然这种方案可以解决问题,但其实两张图片的下载过程并不需要按顺序执行,并发执行它们可以提高执行速度。有个注意点就是必须等两张图片都下载完毕后才能回到主线程显示图片。Dispatch Group能够在这种情况下帮我们提升性能。下面先看看Dispatch Group的用处:
我们可以使用dispatch_group_async函数将多个任务关联到一个Dispatch Group和相应的queue中,group会并发地同时执行这些任务。而且Dispatch Group可以用来阻塞一个线程, 直到group关联的所有的任务完成执行。有时候你必须等待任务完成的结果,然后才能继续后面的处理。
下面用Dispatch Group优化上面的代码:
// 根据url获取UIImage
- (UIImage *)imageWithURLString:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
// 这里并没有自动释放UIImage对象
return [[UIImage alloc] initWithData:data];
}
- (void)downloadImages {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 异步下载图片
dispatch_async(queue, ^{
// 创建一个组
dispatch_group_t group = dispatch_group_create();
__block UIImage *image1 = nil;
__block UIImage *image2 = nil;
// 关联一个任务到group
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 下载第一张图片
NSString *url1 = @"http://car0.autoimg.cn/upload/spec/9579/u_20120110174805627264.jpg";
image1 = [self imageWithURLString:url1];
});
// 关联一个任务到group
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 下载第一张图片
NSString *url2 = @"http://hiphotos.baidu.com/lvpics/pic/item/3a86813d1fa41768bba16746.jpg";
image2 = [self imageWithURLString:url2];
});
// 等待组中的任务执行完毕,回到主线程执行block回调
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
self.imageView1.image = image1;
self.imageView2.image = image2;
// 千万不要在异步线程中自动释放UIImage,因为当异步线程结束,异步线程的自动释放池也会被销毁,那么UIImage也会被销毁
// 在这里释放图片资源
[image1 release];
[image2 release];
});
// 释放group
dispatch_release(group);
});
}
dispatch_group_notify函数用来指定一个额外的block,该block将在group中所有任务完成后执行