这篇文章主要介绍“iOS GCD之dispatch_group_enter和dispatch_group_leave怎么使用”,在日常操作中,相信很多人在iOS GCD之dispatch_group_enter和dispatch_group_leave怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”iOS GCD之dispatch_group_enter和dispatch_group_leave怎么使用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
如何使用
通过一个例子来看下如何使用 dispatch_group_enter 和 dispatch_group_leave:
{ // 首先 需要创建一个线程组 dispatch_group_t group = dispatch_group_create(); // 任务1 dispatch_group_enter(group); NSURLSessionDataTask *task1 = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@""] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"任务1完成"); dispatch_group_leave(group); }]; [task1 resume]; // 任务2 dispatch_group_enter(group); NSURLSessionDataTask *task2 = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@""] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"任务2完成"); dispatch_group_leave(group); }]; [task2 resume]; // 全部完成 dispatch_group_notify(group, dispatch_get_main_queue(), ^(){ NSLog(@"全部完成"); });}
任务1和任务2执行完成之后,才会执行全部完成中的任务。
**注意:**在使用时,dispatch_group_enter 和 dispatch_group_leave 需要成对出现,如果 dispatch_group_leave 的调用次数多于 dispatch_group_enter 的调用次数,程序会 crash。相反,虽然不会发生 crash , 但可能不会达到预期效果。
crash 场景分析
使用场景是,需要异步获取多个图片封面,所有都获取完成后,在执行指定任务,代码示例如下:
- (void)fetchCovers { dispatch_queue_t queue = dispatch_queue_create("com.demo.xxx", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); for (int i = 0; i < 40; ++i) { dispatch_group_enter(group); dispatch_async(queue, ^{ [self fetchCoverByPHAsset:asset targetSize:CGSizeMake(200, 200) resultHandler:^(UIImage * _Nonnull, NSDictionary * _Nonnull, BOOL) { dispatch_group_leave(group); }]; }); } dispatch_group_notify(group, dispatch_get_main_queue(), ^(){ // 全部完成后 执行指定任务 });}- (void)fetchCover:(PHAsset *)asset targetSize:(CGSize)targeSize resultHandler:(void (^)(UIImage * _Nonnull, NSDictionary * _Nonnull, BOOL))resultHandler { PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; if (@available(iOS 14.0, *)) { options.version = PHImageRequestOptionsVersionCurrent; } options.networkAccessAllowed = YES; [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targeSize contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) { if (resultHandler) { resultHandler(result, info, [[info objectForKey:PHImageResultIsDegradedKey] boolValue]); } }];}
这里有个小 tips : 在使用 requestImageForAsset 获取图片时,如果 options 的 deliveryMode 属性使用默认值,在异步获取图片时,其回调可能会走2次。解决方案是将其显示设置为 PHImageRequestOptionsDeliveryModeHighQualityFormat 或 PHImageRequestOptionsDeliveryModeFastFormat。
options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat; // 或 PHImageRequestOptionsDeliveryModeHighQualityFormat
因为获取图片封面的回调可能会走 2 次,从而导致 dispatch_group_leave 调用次数多于 dispatch_group_enter 的调用次数,因此可能会发生 crash。
源码实现
dispatch_group_enter
void dispatch_group_enter(dispatch_group_t dg){ uint32_t old_bits = os_atomic_sub_orig2o(dg, dg_bits, DISPATCH_GROUP_VALUE_INTERVAL, acquire); uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK; if (unlikely(old_value == 0)) { _dispatch_retain(dg); // <rdar://problem/22318411> } if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) { DISPATCH_CLIENT_CRASH(old_bits, "Too many nested calls to dispatch_group_enter()"); }}
dispatch_group_leave
void dispatch_group_leave(dispatch_group_t dg){ uint64_t new_state, old_state = os_atomic_add_orig2o(dg, dg_state, DISPATCH_GROUP_VALUE_INTERVAL, release); uint32_t old_value = (uint32_t)(old_state & DISPATCH_GROUP_VALUE_MASK); if (unlikely(old_value == DISPATCH_GROUP_VALUE_1)) { old_state += DISPATCH_GROUP_VALUE_INTERVAL; do { new_state = old_state; if ((old_state & DISPATCH_GROUP_VALUE_MASK) == 0) { new_state &= ~DISPATCH_GROUP_HAS_WAITERS; new_state &= ~DISPATCH_GROUP_HAS_NOTIFS; } else { new_state &= ~DISPATCH_GROUP_HAS_NOTIFS; } if (old_state == new_state) break; } while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state, old_state, new_state, &old_state, relaxed))); return _dispatch_group_wake(dg, old_state, true); } if (unlikely(old_value == 0)) { DISPATCH_CLIENT_CRASH((uintptr_t)old_value, "Unbalanced call to dispatch_group_leave()"); }}
到此,关于“iOS GCD之dispatch_group_enter和dispatch_group_leave怎么使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!