废话不多说,直接从GCD所提供的函数说起。
dispatch_once
相信大家对单例模式可谓是司空见惯了吧,常见的实现方式是:在某个类中编写名为sharedInstance的类方法,该方法只会返回该类共用的单例实例,而不会在每次调用时都创建新的实例。假设有个类为FTHImage,一般会这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #import "FTHImage.h" @implementation FTHImage + (FTHImage *)sharedImage { static FTHImage *shareImage = nil; @synchronized (self) { if (!shareImage) { shareImage = [[self alloc] init]; } } return shareImage; } @end
|
但是这种单例实现并不是线程安全的,单例实例化代码会执行多次,而不是一次。还好,GCD引入一项特性,即dispatch_once函数。
1
| void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
|
- 此函数有个类型为dispatch_once_t 的参数(其实可以将它看作是一个“标记”)以及一个块参数。
- 使用dispatch_once简化了代码而且彻底保证线程安全,开发者无需担心加锁以及同步的问题,因为GCD都会帮你在底层处理搞定。
- 另外每次调用都必须使用完全相同的标记,所以标记应该声明为static,由此可以保证编译器在每次执行单例方法时都会复用这个变量,从而不会创建新的变量。
再次说明,编写只需执行一次的线程安全代码,通过GCD提供的dispatch_once函数就可以实现。并且在性能方面,比采用@synchronized的方式来得更高效。
用此函数改写上面的代码:
1 2 3 4 5 6 7 8 9 10 11 12
| #import "FTHImage.h" @implementation FTHImage + (FTHImage *)sharedImage { static FTHImage *shareImage = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shareImage = [[self alloc] init]; }); return shareImage; } @end
|
dispatch_async
当你需要在后台执行一个基于网络或 CPU 紧张的任务时就使用 dispatch_async ,这样就不会阻塞当前线程。dispatch_async 添加一个 Block 到队列就立即返回了。任务会在之后由 GCD 决定执行。使用如下:
1 2 3 4 5 6 7
| dispatch_async(queue, ^{ dispatch_async( dispatch_get_main_queue(), ^{ }); });
|
来个简单的例子,好吧,还是以图片下载后显示为例。
1 2 3 4 5 6 7 8 9 10 11 12
| dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSURL *url = [NSURL URLWithString:@"http://xx.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; }); });
|
dispatch_after
dispatch_after用来延后将操作放入队列。
1 2 3 4
| dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (5.0 * NSEC_PER_SEC)) dispatch_after(time, dispatch_get_main_queue(), ^(void) { [self doSomething] })
|
dispatch_after其实就是一个延迟版的 dispatch_async。
其它待续。