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