欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS?GCD之dispatch_group_enter和dispatch_group_leave使用

 更新時間:2023年03月30日 15:52:43   作者:iOS學(xué)習(xí)社區(qū)  
這篇文章主要為大家介紹了iOS?GCD之dispatch_group_enter和dispatch_group_leave使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

在實際開發(fā)中,經(jīng)常需要在幾個任務(wù)全部執(zhí)行完成之后,在執(zhí)行后續(xù)操作,在 iOS 中,我們可以通過 NSOperation 等達(dá)到這一目的。在本篇文章中,我們會介紹如何通過 dispatch_group_enterdispatch_group_leave 來實現(xiàn)這一功能,以及使用過程中遇到的坑。

如何使用

通過一個例子來看下如何使用 dispatch_group_enterdispatch_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 &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使用的詳細(xì)內(nèi)容,更多關(guān)于iOS GCD使用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論