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