关于GCD的一些小事

GCD确实不是什么新东西,我刚开始学iOS开发的时候,也用过皮毛。但终归不是深入学习,之前开发的时候,遇到了一个view需要同时GET三个http的数据。如果一个一个按顺序来,实在是太浪费时间,也太敷衍了事了。之前调用SkyDrive API的时候,我就发现连续几次这样调用API,等待的时间确实有点长,网速慢的话更难以接受。我其实也可以直接使用ASIHttpRequest,但是我认为,如果每次都要从开发中学会新的东西,那么就应该大胆使用自己之前不曾用过的方法。或者通过这些对比,可以找到最好的方法,到时再固定使用也不迟。

项目需求具体为同时加载3个列表的数据,本来GCD直接使用3个异步,的确可以完成加载的任务。不过问题是,在加载的时候我禁用了UI的响应,加载完成之后,我必须重新启用UI响应。因此,3个异步进程完成后,需要再执行一些任务。显然,直接3个异步,可以通过使用一个外部变量来计数,但是这种方法有点粗暴。所以,我查阅了GCD的资料,发现有dispatch_group_t这个东西,通过先创建一个group,等这个group完成之后可以执行对应的方法。

例子代码如下:

 1     dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 2     dispatch_group_t group = dispatch_group_create();
 3     for(id obj in array)
 4         dispatch_group_async(group, queue, ^{
 5             [self doSomethingIntensiveWith:obj];
 6         });
 7     dispatch_group_notify(group, queue, ^{
 8         [self doSomethingWith:array];
 9     });
10     dispatch_release(group);

只要将dispatch_group_async调用对应的异步block,那么当所有异步block都执行完时就会执行dispatch_group_notify的block。整段代码结构清晰,也不需要那些多余的控制变量。不过值得注意是,在ARC下也要记得release那个group,因为它不是NSObject层的对象。

 

上面是同时并行的例子,下面后有是一个控制信号量的例子。信号量,我之前也不知道是什么(不详细解释,具体问谷歌),其实自己也可以手动实现,不过如果用循环来作为阻塞是会浪费大量资源的。GCD提供了一种粒度更细的控制方法:

1 dispatch_semaphore_t __block sem = dispatch_semaphore_create(1);
2 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
3 
4 //运行代码                
5                 
6 dispatch_semaphore_signal(sem);

这段代码要在子线程执行,在异步时,后面的代码就会等待前面的执行完才执行。dispatch_semaphore_create(1) 是返回信号量为1的dispatch_semaphore_t,也就是同时只能有1个异步的执行。dispatch_semaphore_wait()在信号量大于0时,则执行下面的代码并将信号量-1;否则就看dispatch_time_t的timeout,如果为DISPATCH_TIME_FOREVER就会一直阻塞,DISPATCH_TIME_NOW则直接跳过。dispatch_semaphore_signal()就是将信号量+1。利用这个方法,就可以更好地控制block的异步执行。

之前在http://www.cnblogs.com/ipinka/archive/2012/09/03/2660924.html的多选图片是通过锁来实现的,不是太优美,应该说其方法略显老旧。学习了信号量的方法,就可以写成下面这种风骚的写法:

 1 - (NSData *) imageData
 2 {
 3     NSAssert(![NSThread isMainThread], @"can't be called on the main thread due to ALAssetLibrary limitations");
 4     
 5     dispatch_semaphore_t __block sema = dispatch_semaphore_create(0);
 6     __block NSData *returnData;
 7     
 8     ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
 9     {
10         ALAssetRepresentation *rep = [myasset defaultRepresentation];           //获取默认的representation
11         CGImageRef iref = [rep fullResolutionImage];                            //获取全尺寸的CGImage
12         UIImage *tmpImage = [UIImage imageWithCGImage:iref];                    //转换成UIImage
13         
14         if (iref) {
15             if ([self.fileType isEqualToString:@"jpg"])                         //通过判断分别转为jpg或者png
16                 returnData = UIImageJPEGRepresentation(tmpImage, 1.0);
17             else
18                 returnData = UIImagePNGRepresentation(tmpImage);
19         }
20         NSLog(@"imageData Succeed");
21         dispatch_semaphore_signal(sema);
22     };
23     
24     ALAssetsLibraryAccessFailureBlock failureblock  = ^(NSError *myerror)
25     {
26         NSLog(@"imageData Failed");
27         dispatch_semaphore_signal(sema);
28     };
29     
30     
31     ALAssetsLibrary *assetslibrary = [[ALAssetsLibrary alloc] init];
32     [assetslibrary assetForURL:self.imageReferenceURL 
33                    resultBlock:resultblock
34                   failureBlock:failureblock];
35     
36     dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
37     
38     return returnData;
39 }

感觉真的简洁明了,实际运行后效果与之前的毫无差异,以后就可以放心大胆地利用类似的方法来进行多线程的控制了。

posted @ 2013-10-03 02:38  Pinka  阅读(565)  评论(0编辑  收藏  举报