1

У меня есть async NSOperation для загрузки данных для нескольких объектов ImageFile. Поскольку все это происходит асинхронно, я использую группу отправки для отслеживания запросов, а затем dispatch_group_notify для завершения операции, когда все это делается.Что происходит с unmatched dispatch_group_enter и dispatch_group_leave?

Вопрос, который у меня есть, заключается в том, что происходит, когда операция заканчивается преждевременно, либо путем отмены, либо какой-либо другой ошибкой. Группа отправки будет оставлена ​​с непревзойденными 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]; 
     }); 
    }]; 
} 

ответ

2

От 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] в этих случаях.