Примите меры, когда два отдельных NSFetchRequests завершены


Я использую удаленную базу данных с основными данными, и когда я выполняю следующие запросы выборки, в зависимости от подключения к интернету, это может занять некоторое время. Я хотел бы отслеживать эти два запроса и, когда они будут завершены-успешно или неудачно-я хотел бы запустить другой метод.

FetchRequest 1:

 [self.managedObjectContext executeFetchRequest:fetchRequest1 onSuccess:^(NSArray *results) {
        //Succcess
        [self.refreshControl endRefreshing];

    } onFailure:^(NSError *error) {
        [self.refreshControl endRefreshing];
    }];

FetchRequest 2:

 [self.managedObjectContext executeFetchRequest:fetchRequest2 onSuccess:^(NSArray *results) {
        //Succcess
        [self.refreshControl endRefreshing];

    } onFailure:^(NSError *error) {
        [self.refreshControl endRefreshing];
    }];

Я хотел бы подождать, пока запросы fetch 1 и еще 2 оба завершены раньше вызов другого метода.

Могу ли я использовать NSOperationQueue для мониторинга обоих блоков? Если нет, то как лучше всего узнать, когда оба блока завершены?

1 2

1 ответ:

Когда у вас есть асинхронные задачи с зависимостями, у вас есть несколько вариантов:

  1. Самым простым решением с наименьшими изменениями кода является использование семафора:
    // create a semaphore
    
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    // initiate two requests (signaling when done)
    
    [self.managedObjectContext executeFetchRequest:fetchRequest1 onSuccess:^(NSArray *results) {
        [self.refreshControl endRefreshing];
        dispatch_semaphore_signal(semaphore);
    } onFailure:^(NSError *error) {
        [self.refreshControl endRefreshing];
        dispatch_semaphore_signal(semaphore);
    }];
    
    [self.managedObjectContext executeFetchRequest:fetchRequest2 onSuccess:^(NSArray *results) {
        [self.refreshControl endRefreshing];
        dispatch_semaphore_signal(semaphore);
    } onFailure:^(NSError *error) {
        [self.refreshControl endRefreshing];
        dispatch_semaphore_signal(semaphore);
    }];
    
    // now create task to to wait for these two to finish signal the semaphore
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
        // wait for the two signals from the two fetches to be sent
    
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
        // now do whatever you want when those two requests finish
    
        // if you need to do any UI update or do any synchronizing with the main queue, just dispatch this to the main queue
    
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"all done");
        });
    });
    
    Этот подход может быть самым простым, но влечет за собой всевозможные ограничения. Например, это свяжет рабочий поток, ожидающий, пока два других отправят сигнал, поэтому вы должны быть уверены, что у вас не слишком много этих наборов запросов, идущих одновременно. У вас тоже есть быть уверенным, что эти запросы вызовут либо onSuccess, либо onFailure, но никогда не оба и всегда один. Это также не дает возможности отмены или возможности самостоятельно ограничить степень параллелизма. Но вы можете сделать это с минимальными изменениями кода.
  2. Второй подход заключается в замене асинхронных запросов синхронными, что позволяет использовать стандартную логику NSOperation addDependency:

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
        // completion operation
    }];
    
    NSOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        // do fetch1 _synchronously_
    }];
    
    [queue addOperation:operation1];
    
    NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        // do fetch2 _synchronously_
    }];
    
    [queue addOperation:operation2];
    
    [completionOperation addDependencies:@[operation1, operation2]];
    [queue addOperation:completionOperation];
    

    Этот подход требует, чтобы ваши синхронные выборки потокобезопасны. Я не знаком с этим API, который вы используете, поэтому я не могу говорить об этом.

  3. Если у вас нет синхронных представлений ваших запросов на выборку, которые вы могли бы добавить в очередь, третий подход будет заключаться в том, чтобы обернуть ваши асинхронные запросы на выборку с вашим собственным параллельным подклассом NSOperation, который не будет сигнализировать isFinished, пока не будет выполнена асинхронная операция (а также предположительно вызвать ваши собственные блоки onSuccess и onFailure). Как только вы сделаете это, вы сможете затем используйте функцию setDependency (как показано в предыдущем пункте), чтобы сделать вашу третью операцию зависимой от двух других окончаний. Дополнительные сведения см. В разделеконфигурирование операций для параллельного выполнения руководства по программированию с параллелизмом .

Я хотел бы дать более точный ответ,но я недостаточно знаком с опциями / ограничениями, связанными с вашей параллельной управляемой контекстной библиотекой.