iOS?GCD之dispatch_group_enter和dispatch_group_leave使用
正文
在實際開發(fā)中,經(jīng)常需要在幾個任務(wù)全部執(zhí)行完成之后,在執(zhí)行后續(xù)操作,在 iOS 中,我們可以通過 NSOperation 等達(dá)到這一目的。在本篇文章中,我們會介紹如何通過 dispatch_group_enter 和 dispatch_group_leave 來實現(xiàn)這一功能,以及使用過程中遇到的坑。
如何使用
通過一個例子來看下如何使用 dispatch_group_enter 和 dispatch_group_leave:
{ // 首先 需要創(chuàng)建一個線程組 dispatch_group_t group = dispatch_group_create(); // 任務(wù)1 dispatch_group_enter(group); NSURLSessionDataTask *task1 = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@""] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"任務(wù)1完成"); dispatch_group_leave(group); }]; [task1 resume]; // 任務(wù)2 dispatch_group_enter(group); NSURLSessionDataTask *task2 = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@""] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"任務(wù)2完成"); dispatch_group_leave(group); }]; [task2 resume]; // 全部完成 dispatch_group_notify(group, dispatch_get_main_queue(), ^(){ NSLog(@"全部完成"); }); }
任務(wù)1和任務(wù)2執(zhí)行完成之后,才會執(zhí)行全部完成中的任務(wù)。
**注意:**在使用時,dispatch_group_enter 和 dispatch_group_leave 需要成對出現(xiàn),如果 dispatch_group_leave 的調(diào)用次數(shù)多于 dispatch_group_enter 的調(diào)用次數(shù),程序會 crash。相反,雖然不會發(fā)生 crash , 但可能不會達(dá)到預(yù)期效果。
crash 場景分析
使用場景是,需要異步獲取多個圖片封面,所有都獲取完成后,在執(zhí)行指定任務(wù),代碼示例如下:
- (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(), ^(){ // 全部完成后 執(zhí)行指定任務(wù) }); } - (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 屬性使用默認(rèn)值,在異步獲取圖片時,其回調(diào)可能會走2次。解決方案是將其顯示設(shè)置為 PHImageRequestOptionsDeliveryModeHighQualityFormat 或 PHImageRequestOptionsDeliveryModeFastFormat。
options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat; // 或 PHImageRequestOptionsDeliveryModeHighQualityFormat
因為獲取圖片封面的回調(diào)可能會走 2 次,從而導(dǎo)致 dispatch_group_leave 調(diào)用次數(shù)多于 dispatch_group_enter 的調(diào)用次數(shù),因此可能會發(fā)生 crash。
源碼實現(xiàn)
- 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使用的詳細(xì)內(nèi)容,更多關(guān)于iOS GCD使用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解iOS中position:fixed吸底時的滑動出現(xiàn)抖動的解決方案
這篇文章主要介紹了詳解iOS中position:fixed吸底時的滑動出現(xiàn)抖動的解決方案,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12iOS應(yīng)用開發(fā)中使用NSLocale類實現(xiàn)對象信息的本地化
這篇文章主要介紹了iOS應(yīng)用開發(fā)中使用NSLocale類實現(xiàn)對象信息的本地化的方法,能夠?qū)r間和貨幣等格式化為與系統(tǒng)本地設(shè)置相同的偏好,需要的朋友可以參考下2016-05-05iOS微信瀏覽器回退不刷新實例(監(jiān)聽瀏覽器回退事件)
下面小編就為大家?guī)硪黄猧OS微信瀏覽器回退不刷新實例(監(jiān)聽瀏覽器回退事件)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05iOS使用UICollectionView實現(xiàn)列表頭部拉伸效果
這篇文章主要介紹了iOS使用UICollectionView實現(xiàn)列表頭部拉伸效果,OC和Swift兩個版本,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05iOS中的UITextView文字輸入光標(biāo)使用技巧小結(jié)
UITextView在用戶體驗方面有著十分明顯的作用,包括鍵盤的呼出及文字的選擇等,接下來就來整理給出一份iOS中的UITextView文字輸入光標(biāo)使用技巧小結(jié),需要的朋友可以參考下2016-05-05iOS利用UIScrollView實現(xiàn)無限滾動效果
這篇文章主要給大家介紹了iOS如何利用UIScrollView實現(xiàn)無限滾動的效果,首先需要說明的是,文本所講的是一種"笨辦法",但是好理解且容易實現(xiàn),在圖片不多的時候用它也無妨。感興趣的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2016-12-12