当前位置: 代码网 > it编程>App开发>苹果IOS > iOS GCD之dispatch_group_enter和dispatch_group_leave使用

iOS GCD之dispatch_group_enter和dispatch_group_leave使用

2024年06月01日 苹果IOS 我要评论
正文在实际开发中,经常需要在几个任务全部执行完成之后,在执行后续操作,在 ios 中,我们可以通过 nsoperation 等达到这一目的。在本篇文章中,我们会介绍如何通过 dispatch_grou

正文

在实际开发中,经常需要在几个任务全部执行完成之后,在执行后续操作,在 ios 中,我们可以通过 nsoperation 等达到这一目的。在本篇文章中,我们会介绍如何通过 dispatch_group_enterdispatch_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使用的详细内容,更多关于ios gcd使用的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com