文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

iOS GCD之dispatch_group_enter和dispatch_group_leave怎么使用

2023-07-05 19:35

关注

这篇文章主要介绍“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。

源码实现

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()");    }}
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怎么使用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯