iOS GCD之dispatch_group_enter和dispatch_group_leave怎么使用

这篇文章主要介绍“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_enterdispatch_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 &amp; 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 &amp; disPATCH_GROUP_VALUE_MASK) == 0) {
    			new_state &amp;= ~disPATCH_GROUP_HAS_WAITERS;
    			new_state &amp;= ~disPATCH_GROUP_HAS_NOTIFS;
    		} else {
    			new_state &amp;= ~disPATCH_GROUP_HAS_NOTIFS;
    		}
    		if (old_state == new_state) break;
    	} while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state,
    			old_state, new_state, &amp;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怎么使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程之家网站,小编会继续努力为大家带来更多实用的文章

相关文章

支付宝万能福可以替代任何福字,没有集齐五福的用户快来试试...
TV盒子助手是一款可以远程管理智能电视、电视盒子的应用,主...
Kindle无法导入电子书怎么办?Kindle电子书阅读器是一款非常...
蓝奏云是现在很多用户下载资源时都会遇到的一款网盘,用户体...
华为Mate20怎么设置触摸屏幕亮屏?对于全面屏的手机来说,苹...
华为怎么关闭锁屏杂志?华为手机在屏幕锁定之后再次打开会看...