不匹配的 dispatch_group_enter 和 dispatch_group_leave 会发生什么?

What happens with unmatched dispatch_group_enter and dispatch_group_leave?

我有一个异步 NSOperation 可以为多个 ImageFile 对象下载数据。由于这一切都是异步发生的,所以我使用调度组来跟踪请求,然后 dispatch_group_notify 在它们全部完成后完成操作。

我的问题是,当操作因取消或其他错误而提前结束时会发生什么。调度组将留下不匹配的 dispatch_group_enterdispatch_group_leave,因此永远不会调用 dispatch_group_notify。它是系统永远保留在某处的块等待,还是会在 NSOperation 被释放时被释放?

或者我的方法不理想,我还应该怎么做?

- (void)main
{
    if (self.cancelled) {
        [self completeOperation];
        return;
    }

    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    context.persistentStoreCoordinator = self.persistentStoreCoordinator;
    context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

    [context performBlock:^{

        NSFetchRequest *request = [ImageFile fetchRequest];
        request.predicate = ....
        request.sortDescriptors = ...

        NSError *error;
        NSArray *imageFiles = [context executeFetchRequest:request error:&error];
        if (!imageFiles) {
            // Error handling...
            [self completeOperation];
            return;
        }

        dispatch_group_t group = dispatch_group_create();

        for (ImageFile *imageFile in imageFiles) {
            dispatch_group_enter(group);
            @autoreleasepool {
                [self.webService requestImageWithId:imageFile.id completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

                    if (self.cancelled) {
                        [self completeOperation];
                        return;
                    }

                    [context performBlock:^{
                        if (data && !error) {
                            imageFile.data = data;
                            NSError *error;
                            if (![context save:&error]) {
                                // Error handling...
                                [self completeOperation];
                                return;
                            }
                        }
                        dispatch_group_leave(group);
                    }];
                }];
            }
        }

        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            [self completeOperation];
        });
    }];
}

来自docs for dispatch_group_enter():

A call to this function must be balanced with a call to dispatch_group_leave.

来自docs for dispatch_group_t:

The dispatch group keeps track of how many blocks are outstanding, and GCD retains the group until all its associated blocks complete execution.

它谈论的是未完成的块,但它真正的意思是对 dispatch_group_enter().

的不匹配调用

因此,对于您关于所发生情况的问题的答案是分派组对象有效泄漏。传递给 dispatch_group_notify() 的块对象及其具有强引用的任何对象也会泄漏。在您的情况下,这包括 self.

关于您的方法是否 "ideal" 的问题的答案是:不,这并不理想。它甚至不符合 GCD 的设计合同。您 必须 平衡对 dispatch_group_enter() 的所有调用与对 dispatch_group_leave().

的调用

如果你想以某种方式区分成功和失败或正常完成和取消,你应该设置一些可用于通知块的状态,然后编码通知块以参考该状态来决定做什么。

但是,在您的情况下,您未能调用 dispatch_group_leave() 的代码路径只是执行与通知块相同的操作。所以我什至不确定为什么在这些情况下你不只是调用 dispatch_group_leave() 而不是调用 [self completeOperation]