在开发过程中,经常会遇到耗时的操作需要放到子线程中,完成后再返回到主线程中同步UI,以防阻塞主线程。有时候,可能需求是要同时处理多个耗时操作,在处理完所有耗时操作后再同步UI,这时候就可以使用到GCD了。
需求1
同时下载两张图
需要同时下载两张图片,在两张图片都下载完成后,同时返回。
使用group的大致实现
- (void)downloadImageSuccess:(void (^)(UIImage *image1, UIImage *image2))success {
dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
__block UIImage *image1;
__block UIImage *image2;
dispatch_group_async(group, global_queue, ^{
NSLog(@"第一张图片开始下载");
sleep(2);
image1 = [UIImage new];
NSLog(@"第一张图片下载完成");
});
dispatch_group_async(group, global_queue, ^{
NSLog(@"第二张图片开始下载");
sleep(3);
image2 = [UIImage new];
NSLog(@"第二张图片下载完成");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"两张图片下载完成");
success(image1, image2);
});
}
需求二
下载超时时间为2秒
设置图片下载超时时间为2秒,如果图片在2秒后没有全部下载完成,那么没完成的图片用nil返回。
代码实现
使用dispatch_group_wait
函数。
- (void)downloadImageSuccess:(void (^)(UIImage *image1, UIImage *image2))success {
dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
__block UIImage *image1;
__block UIImage *image2;
dispatch_group_async(group, global_queue, ^{
NSLog(@"第一张图片开始下载");
sleep(2);
image1 = [UIImage new];
NSLog(@"第一张图片下载完成");
});
dispatch_group_async(group, global_queue, ^{
NSLog(@"第二张图片开始下载");
sleep(4);
image2 = [UIImage new];
NSLog(@"第二张图片下载完成");
});
dispatch_async(global_queue, ^{
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
dispatch_async(dispatch_get_main_queue(), ^{
success(image1, image2);
});
});
}
2018-12-09 21:56:15.007222+0800 GCDDemo[12279:450516] 第一张图片开始下载
2018-12-09 21:56:15.007222+0800 GCDDemo[12279:450524] 第二张图片开始下载
2018-12-09 21:56:17.011723+0800 GCDDemo[12279:450516] 第一张图片下载完成
2018-12-09 21:56:18.007809+0800 GCDDemo[12279:450477] <UIImage: 0x60000186a450>, {0, 0} (null)
2018-12-09 21:56:19.008913+0800 GCDDemo[12279:450524] 第二张图片下载完成
注意wait一定要放在子线程中,否则就阻塞主线程了。
具体使用GCD完成下载时遇到的问题
问题1:异步任务无法控制
光上面的代码,其实并不能实现多张图片下载的同步功能,因为如果使用系统的封装好的下载的类的话,就只能这样用:
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:@"image"] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
}];
[task resume];
如果直接把这段代码扔到dispatch_group_async
中,那么只能做到异步开始下载任务,并不能做到同步任务的功能。
类似这样:
dispatch_group_async(group, global_queue, ^{
dispatch_sync(con_queue, ^{
// TODO
});
});
那么要怎么去做呢?
答案是使用dispatch_group_enter
和dispatch_group_leave
方法。
具体模拟使用:
dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
__block UIImage *image1;
__block UIImage *image2;
dispatch_group_enter(group);
dispatch_group_async(group, global_queue, ^{
dispatch_sync(con_queue), ^{
NSLog(@"第一张图片开始下载");
sleep(2);
image1 = [UIImage new];
NSLog(@"第一张图片下载完成");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, global_queue, ^{
dispatch_sync(con_queue), ^{
NSLog(@"第二张图片开始下载");
sleep(4);
image2 = [UIImage new];
NSLog(@"第二张图片下载完成");
dispatch_group_leave(group);
});
});
dispatch_async(global_queue, ^{
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
dispatch_async(dispatch_get_main_queue(), ^{
success(image1, image2);
});
});
2018-12-09 22:23:36.918395+0800 GCDDemo[12558:492481] 第一张图片开始下载
2018-12-09 22:23:36.918395+0800 GCDDemo[12558:492483] 第二张图片开始下载
2018-12-09 22:23:38.923074+0800 GCDDemo[12558:492481] 第一张图片下载完成
2018-12-09 22:23:39.919640+0800 GCDDemo[12558:492349] <UIImage: 0x600003ffb100>, {0, 0} (null)
2018-12-09 22:23:40.923513+0800 GCDDemo[12558:492483] 第二张图片下载完成
问题2:如何取消图片下载任务
其实使用SDWebImage的SDWebImageDownloader
类可以直接取消下载操作。
同时我看到NSURLSessionDownloadTask
类中也有cancel方法,但是我没试。。。
最后,我的博客地址:http://jabberyq.top/