Увеличение переменной из асинхронного блока в Objective-C - PullRequest
0 голосов
/ 13 сентября 2018

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

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

Я в полной растерянности. Кто-нибудь может предложить подход для решения этой проблемы?

Обновление: Как указал Мэтт ниже, основная проблема здесь заключается в том, как контролировать время запросов. Проведя еще несколько исследований, я выяснил, почему мой оригинальный код не работал. Как выясняется, интервал тайм-аута запускается, как только начинается первая задача, и по истечении времени любые дополнительные запросы не будут выполняться. Если вы точно знаете, сколько времени займет выполнение всех ваших запросов, можно просто увеличить время ожидания для ваших запросов. Однако лучший подход заключается в использовании NSOperationQueue для контроля за отправкой запросов. Прекрасный пример того, как это сделать, см .: https://code -examples.net / en / q / 19c5248 Если вы используете этот подход, имейте в виду, что вам придется вызывать метод completeOperation () для каждой операции, которую вы создаете в обработчике завершения загрузки.

Пример кода:

-(void) downloadSkuImages:(NSArray *) imagesToDownload onComplete:(void (^)(BOOL update,NSError *error))onComplete
{
    [self runSerializedRequests:imagesToDownload progress:weakProgress downloaded:0 index:0 onComplete:onComplete ];
}

-(void)runSerializedRequests:(NSArray *) skuImages progress:(NSProgress *) progress downloaded:(int) totalDownloaded index:(NSUInteger) index onComplete:(void (^)(BOOL update,NSError *error))onComplete 
{
     int __block downloaded = totalDownloaded;

     TotalDownloadProgressBlock totalDownloadProgressBlock =  ^BOOL (SkuImageID *skuImageId, NSString  *imageFilePath, NSError *error) {
          if(error==nil) {
                  downloaded++;
                  weakProgress.completedUnitCount = downloaded;
                  //save change to core-data here
                  }
          else {
                        downloaded++;
                        weakProgress.completedUnitCount = downloaded;
                        [weakSelf setSyncOperationDetail:[NSString stringWithFormat:@"Problem downloading sku image %@",error.localizedDescription]];
                      }

          if(weakProgress.totalUnitCount==weakProgress.completedUnitCount) {
                              [weakSelf setSyncOperationIndicator:SYNC_INDICATOR_WORKING];
                              [weakSelf setSyncOperationDetail:@"All product images up to date"];
                              [weakSelf setSyncOperationStatus:SYNC_STATUS_SUCCESS];
                              weakProgress.totalUnitCount = 1;
                              weakProgress.completedUnitCount = 1;
                              onComplete(false,nil);
                              return true;
                          }
          return false;
     };

    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:nil
    completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {

                NSLog(@"finished download %u of %lu", index +1, (unsigned long)skuImages.count);
                if(error != nil)
                {                    
                    NSLog(@"Download failed for URL: %@ with error: %@",skuImage.url, error.localizedDescription);
                }
                else
                {
                    NSLog(@"Download succeeded for URL: %@", skuImage.url);
                }
                dispatch_async(dispatch_get_main_queue(), ^(void){

                    totalDownloadProgressBlock(skuImageId, imageFilePath, error);

                });

                [self runSerializedRequests:manager skuImages:skuImages progress:progress downloaded:downloaded index:index+1 onComplete:onComplete ];
            }];

            NSLog(@"Starting download %u of %lu", index +1, (unsigned long)skuImages.count);
            [downloadTask resume];
}

1 Ответ

0 голосов
/ 13 сентября 2018

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

Но это никогда не был правильный способ решения проблемы.Используйте один постоянный пользовательский NSURLSession со своей собственной конфигурацией и установите для конфигурации httpMaximumConnectionsPerHost.

...