瓦解形貌
克日排查线上瓦解时,发现一个形貌信息很少的瓦解,如上。由dispatch_group_leave.cold.1可知,属于dispatch_group非常
dispatch_group利用
dispatch_group利用场景:A任务依赖B/C/D子任务全部实验完成,才举行触发实验。
怎样添加子任务,通常有两种方式:
- block
- dispatch_group_enter+dispatch_group_leave
dispatch_group_t group = dispatch_group_create(); // 添加N个子任务... dispatch_group_enter(group); dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 耗时任务task1 dispatch_group_leave(self.group); }); // ... dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"task123完成,收到通知"); });为什么dispatch_group_leave瓦解?
dispatch_group维持有一个计数。
- dispatch_group_create初始化为LONG_MAX
- dispatch_group_leave时+1
- dispatch_group_enter时-1。
当leave次数超出enter次数时,LONG_MAX+1溢出,触发Crash。
陷阱规避
1. 属性记录dispatch_group_t,易引发错位leave
示例错误代码:
// 强引用group对象@property (nonatomic, strong) dispatch_group_t group;@property (nonatomic, strong) NSOperationQueue *queue;- (void)cancel { self.cancelled = YES; [self.queue cancelAllOperations];}- (void)exportImageNSArray<NSString *> *)infos { self.group = dispatch_group_create(); self.cancelled = NO; __weak typeof(self) weak_self = self; for (NSString *info in infos) { dispatch_group_enter(self.group); [self.queue addOperationWithBlock:^{ // 大量盘算与io耗时操纵 // ... dispatch_group_leave(weak_self.group); }]; } dispatch_group_notify(self.group, dispatch_get_main_queue(), ^{ if (!weak_self.cancelled) { // 导出完成,下一步 } });}- (NSOperationQueue *)queue { if (!_queue) { _queue = [[NSOperationQueue alloc] init]; _queue.maxConcurrentOperationCount = 2; } return _queue;}复现步调:
Demo,点击开始,4s内点击竣事再点击开始,等待一会,瓦解。
如果infos只有一条数据,单个导出任务耗时10s,任务A在举行到5s时,取消任务A,重新触发新的导出B,A/B均完成时,就会瓦解。
缘故因由分析:
- NSOperationQueue已经调治的任务,实在是无法直接取消的,任务仍会继续实验
- A任务再履历5s后完成,调用dispatch_group_leave(weak_self.group);,但实在A创建的group已开释,调用的是B的group.leave,计数为LONG_MAX,并触发notify。
- 当B也完成任务时,再次调用dispatch_group_leave,导致瓦解。
2. 怎样规避?
dispatch_group_t改用局部变量即可。
示例精确代码:
// 强引用group对象@property (nonatomic, strong) NSOperationQueue *queue;- (void)cancel { self.cancelled = YES; [self.queue cancelAllOperations];}- (void)exportImageNSArray<NSString *> *)infos { dispatch_group_t group = dispatch_group_create(); self.cancelled = NO; __weak typeof(self) weak_self = self; for (NSString *info in infos) { dispatch_group_enter(group); [self.queue addOperationWithBlock:^{ // 大量盘算与io耗时操纵 // ... dispatch_group_leave(group); }]; } dispatch_group_notify(.group, dispatch_get_main_queue(), ^{ if (!weak_self.cancelled) { // 导出完成,下一步 } });}- (NSOperationQueue *)queue { if (!_queue) { _queue = [[NSOperationQueue alloc] init]; _queue.maxConcurrentOperationCount = 2; } return _queue;} |