1.Dispatch Group
在追加到 Dispatch Queue 中的多個(gè)任務(wù)處理完畢之后想執(zhí)行結(jié)束處理,這種需求會(huì)經(jīng)常出現(xiàn)。如果只是使用一個(gè) Serial Dispatch Queue (串行隊(duì)列)時(shí),只想要將執(zhí)行的處理全部追加到改串行隊(duì)列中并在最后追加結(jié)束處理即可,但是在使用 Concurrent Queue 時(shí),可能會(huì)同時(shí)使用多個(gè) Dispatch Queue 時(shí),源碼就會(huì)變得很復(fù)雜。
這種情況下,就可以使用 Dispatch Group。
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.wangwang.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 1000; i++) {
if (i == 999) {
NSLog(@"11111");
}
}
});
dispatch_group_async(group, queue, ^{
NSLog(@"22222222");
});
dispatch_group_async(group, queue, ^{
NSLog(@"3333");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"done");
});
因?yàn)橄?Concurrent Dispatch Queue 追加處理,多個(gè)線(xiàn)程并行執(zhí)行,所以追加處理的執(zhí)行順序不定。執(zhí)行順序會(huì)發(fā)生變化,但是執(zhí)行結(jié)果的 done 一定是最后輸出的。
無(wú)論向什么樣的 Dispatch Queue 中追加處理,使用 Dispatch Group 都可以監(jiān)視這些處理執(zhí)行的結(jié)果。一旦監(jiān)測(cè)到所有的處理執(zhí)行結(jié)束,就可以將結(jié)束的處理追加到 Dispatch Queue 中,這就是使用 Dispatch Group 的原因。
下面是一個(gè)使用 Dispatch Group 異步下載兩張圖片,然后合并成一張圖片的 demo。
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) UIImage *imageOne;
@property(nonatomic, strong) UIImage *imageTwo;
@property(nonatomic, weak) UILabel *textLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self operation1];
}
- (void)operation1
{
UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(200, 450, 0, 0)];
textLabel.text = @"正在下載圖片";
[textLabel sizeToFit];
[self.view addSubview:textLabel];
self.textLabel = textLabel;
[self group];
NSLog(@"在下載圖片的時(shí)候,主線(xiàn)程貌似還可以干點(diǎn)什么");
}
- (void)group
{
UIImageView *imageView = [[UIImageView alloc] init];
[self.view addSubview:imageView];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create(
"cn.gcd-group.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
NSLog(@"正在下載第一張圖片");
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"[http://images2015.cnblogs.com/blog/471463/201509/471463-20150912213125372-589808688.png](http://images2015.cnblogs.com/blog/471463/201509/471463-20150912213125372-589808688.png)"
]];
NSLog(@"第一張圖片下載完畢");
self.imageOne = [UIImage imageWithData:data];
});
dispatch_group_async(group, queue, ^{
NSLog(@"正在下載第二張圖片");
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@[http://images2015.cnblogs.com/blog/471463/201509/471463-20150912212457684-585830854.png(http://images2015.cnblogs.com/blog/471463/201509/471463-20150912212457684-585830854.png)"]];
NSLog(@"第二張圖片下載完畢");
self.imageTwo = [UIImage imageWithData:data];
});
dispatch_group_notify(group, queue, ^{
UIGraphicsBeginImageContext(CGSizeMake(300, 400));
[self.imageOne drawInRect:CGRectMake(0, 0, 150, 400)];
[self.imageTwo drawInRect:CGRectMake(150, 0, 150, 400)];
UIImage *newImage =UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
UIImageView *imageView = [[UIImageView alloc] initWithImage:newImage];
[self.view addSubview:imageView];
self.textLabel.text = @"圖片合并完畢";
});
});
}
@end
2. Dispatch_barrier_async
在訪問(wèn)數(shù)據(jù)庫(kù)或者文件的時(shí)候,我們可以使用Serial Dispatch Queue可避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,代碼如下所示:
其實(shí)使用GCD可以簡(jiǎn)單高效的代替同步塊或者鎖對(duì)象,可以使用,串行同步隊(duì)列,將讀操作以及寫(xiě)操作都安排在同一個(gè)隊(duì)列里,即可保證數(shù)據(jù)同步,代碼如下:
多個(gè)getter方法(也就是讀?。┦强梢圆l(fā)執(zhí)行的,而getter(讀)與setter(寫(xiě))方法是不能并發(fā)執(zhí)行的,利用這個(gè)特點(diǎn),還能寫(xiě)出更快的代碼來(lái),這次注意,不用串行隊(duì)列,而改用并行隊(duì)列:
#import <Foundation/Foundation.h>
@interface ZYPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end
#import "ZYPerson.h"
@interface ZYPerson ()
@end
static NSString *_name;
static dispatch_queue_t _concurrentQueue;
@implementation ZYPerson
- (instancetype)init
{
if (self = [super init]) {
_concurrentQueue = dispatch_queue_create("com.person.syncQueue"
, DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (void)setName:(NSString *)name
{
dispatch_barrier_async(_concurrentQueue, ^{
_name = [name
copy
];
});
}
- (NSString *)name
{
__block NSString *tempName;
dispatch_sync(_concurrentQueue, ^{
tempName = _name;
});
return tempName;
}
@end
這樣優(yōu)化,可以發(fā)現(xiàn)這種做法肯定比使用串行隊(duì)列要快。
在這個(gè)代碼中,我用了點(diǎn)新的東西,dispatch_barrier_async,可以翻譯成柵欄(barrier),它可以往隊(duì)列里面發(fā)送任務(wù)(塊,也就是block),這個(gè)任務(wù)有柵欄(barrier)的作用。
在隊(duì)列中,barrier塊必須單獨(dú)執(zhí)行,不能與其他block并行。這只對(duì)并發(fā)隊(duì)列有意義,并發(fā)隊(duì)列如果發(fā)現(xiàn)接下來(lái)要執(zhí)行的block是個(gè)barrier block,那么就一直要等到當(dāng)前所有并發(fā)的block都執(zhí)行完畢,才會(huì)單獨(dú)執(zhí)行這個(gè)barrier block代碼塊,等到這個(gè)barrier block執(zhí)行完畢,再繼續(xù)正常處理其他并發(fā)block。在上面的代碼中,setter方法中使用了barrier block以后,對(duì)象的讀取操作依然是可以并發(fā)執(zhí)行的,但是寫(xiě)入操作就必須單獨(dú)執(zhí)行了。