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

时间:2017-02-15 01:47:16

标签: objective-c asynchronous grand-central-dispatch nsoperation

我有一个异步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];
        });
    }];
}

1 个答案:

答案 0 :(得分:3)

来自docs for dispatch_group_enter()

  

必须通过调用dispatch_group_leave来平衡对此功能的调用。

来自docs for dispatch_group_t

  

调度组会跟踪未完成的块数,并且GCD会保留该组,直到其所有关联的块完成执行。

它讨论了未完成的块,但它真正意味着对dispatch_group_enter()的无法匹配的调用。

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

关于你的方法是否理想的问题的答案"是:不,它不理想。它甚至不适用于GCD的设计合同。您必须通过调用dispatch_group_enter()来平衡对dispatch_group_leave()的所有来电。

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

但是,在您的情况下,您无法调用dispatch_group_leave()的代码路径只是执行通知块所做的事情。因此,我甚至不确定为什么您不仅要调用dispatch_group_leave()而不是在这些情况下调用[self completeOperation]