我有一个异步NSOperation
来下载多个ImageFile
对象的数据。由于这一切都是异步发生的,我使用调度组来跟踪请求,然后dispatch_group_notify
完成操作。
我遇到的问题是当操作提前结束时,通过取消或其他一些错误会发生什么。调度组将保留不匹配的dispatch_group_enter
和dispatch_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];
});
}];
}
答案 0 :(得分:3)
来自docs for dispatch_group_enter()
:
必须通过调用
dispatch_group_leave
来平衡对此功能的调用。
调度组会跟踪未完成的块数,并且GCD会保留该组,直到其所有关联的块完成执行。
它讨论了未完成的块,但它真正意味着对dispatch_group_enter()
的无法匹配的调用。
因此,关于所发生情况的问题的答案是调度组对象有效泄漏。块对象传递给dispatch_group_notify()
,并且它具有强引用的任何对象也会泄漏。在您的情况下,包括self
。
关于你的方法是否理想的问题的答案"是:不,它不理想。它甚至不适用于GCD的设计合同。您必须通过调用dispatch_group_enter()
来平衡对dispatch_group_leave()
的所有来电。
如果您想以某种方式区分成功与失败或正常完成和取消,您应该设置一些可用于通知块的状态,然后对通知块进行编码以咨询该状态以决定该做什么。
但是,在您的情况下,您无法调用dispatch_group_leave()
的代码路径只是执行通知块所做的事情。因此,我甚至不确定为什么您不仅要调用dispatch_group_leave()
而不是在这些情况下调用[self completeOperation]
。