不匹配的 dispatch_group_enter 和 dispatch_group_leave 会发生什么?
What happens with unmatched dispatch_group_enter and dispatch_group_leave?
我有一个异步 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];
});
}];
}
来自docs for dispatch_group_enter()
:
A call to this function must be balanced with a call to dispatch_group_leave
.
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]
。
我有一个异步 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];
});
}];
}
来自docs for dispatch_group_enter()
:
A call to this function must be balanced with a call to
dispatch_group_leave
.
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]
。