Ожидание нескольких сетевых асинхронных вызовов в iOS

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

Вот код, который у меня сейчас есть. Фрагмент кода в контроллере представления выглядит следующим образом (переменная result в настоящее время не используется):

- (void)loadData
{
   BOOL __block result = NO;

   dispatch_queue_t queue = dispatch_queue_create(dataLoadQueue, NULL);
   dispatch_async(queue,^{

      Loader *loader = [[Loader alloc] init];
      [loader loadData];

      dispatch_async(dispatch_get_main_queue(), ^{

        if (result) {
            // Update view and notify success
        }
        else {
            // Update view and notify error
        }
      });
   });

   dispatch_release(queue);
}

А это сторона пользовательского объекта loader:

- (void)loadData
{
   if ([Reachability checkNetStatus]) {

      Service1 *service1 = [[Service1 alloc] init];
      [service1 callAsyncService];

      Service2 *service2 = [[Service2 alloc] init];
      [service2 callAsyncService];

      // More service calls
   }

   else {
      // Notify network not reachable
   }
}

Объекты service1, service2... serviceN соответствуют NSURLConnectionDelegate, и я уведомляю об окончании connectionDidFinishLoading: с помощью NSNotificationCenter (объект loader прослушивает такие уведомления). Тогда я не знаю, как правильно заставить loader ждать всех сетевых операций и уведомлять обратно контроллер представления.

заранее спасибо


person AppsDev    schedule 01.08.2013    source источник
comment
Всегда ли загрузчик создает один и тот же набор объектов службы? Это похоже на ваш опубликованный код, поскольку у метода loadData Loader нет аргументов.   -  person rdelmar    schedule 01.08.2013
comment
@rdelmar да, это так   -  person AppsDev    schedule 01.08.2013


Ответы (3)


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

Что касается того, как Loader узнает, когда все его сетевые операции выполнены, вы можете просто сохранить список строк в изменяемом массиве, например «1 сделано», «2 сделано» и т. д., который будет таким же, как строки, отправленные пользователем. информация об уведомлениях, вызываемых в connectionDidFinishLoading:. Все сервисы могут отправлять одно и то же уведомление, но с разной информацией о пользователе. В селекторе для наблюдателя удалите строку, идентичную той, что указана в информации о пользователе, и проверьте, не пуст ли массив — когда это так, все ваши услуги выполнены. В этот момент я бы использовал метод делегата для передачи данных контроллеру представления. Что-то вроде этого в загрузчике:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.doneStrings = [@[@"1 done", @"2 done", @"3 done", @"4 done"] mutableCopy];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationReceived:) name:@"SeriveFinishedNotification" object:nil];
}

-(void)notificationReceived:(NSNotification *) aNote {
    [self.doneStrings removeObjectIdenticalTo:[aNote.userInfo objectForKey:@"doneString"]];
    if (self.doneStrings.count == 0)
        [delegate doSomethingWithTheData: theData];
}

Вам, вероятно, потребуются некоторые другие вещи, например, обработка случая, когда некоторые из сетевых операций терпят неудачу.

person rdelmar    schedule 01.08.2013

Если вы хотите дождаться выполнения асинхронных задач, вы можете использовать семафор. См. пример ниже, логика довольно проста. Я думаю, вы легко адаптируетесь к своему делу.

//create the semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[objectManager.HTTPClient deletePath:[address addressURL] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

      //some code here executed in background

        dispatch_semaphore_signal(semaphore); //sends a notification to release the semaphore

    }failure:^(AFHTTPRequestOperation *operation, NSError *error) {

       //some other code here also executed in background

        dispatch_semaphore_signal(semaphore); //sends a notification to release the semaphore
    }];


//holds the thread until the dispatch_semaphore_signal(semaphore) is send
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
{
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
person Lucas Eduardo    schedule 01.08.2013

Вы не поделились подробностями о том, как работают эти асинхронные запросы, но другой подход состоит в том, чтобы сделать эти асинхронные запросы NSOperation объектами, которые вы отправляете в NSOperationQueue. (AFNetworking является примером реализации такого рода.) Когда вы делаете это, вы можете создать еще один NSOperation, который будет запускаться после завершения операций сетевого запроса, сделав его зависимым от этих операций сетевого запроса. Таким образом, он будет работать только тогда, когда все сетевые запросы будут выполнены. Использование решения на основе NSOperation имеет и другие преимущества (например, вы можете использовать setMaxConcurrentOperationCount, чтобы обеспечить параллелизм, но не запускать слишком много одновременных запросов в любой момент времени).

использованная литература

person Rob    schedule 01.08.2013
comment
Спасибо. Я немного лучше знаком с выбранным мной подходом, чем с NSOperation, но если использование NSOperationQueue может дать преимущества в подобных сценариях, я найду время, чтобы следовать вашим рекомендациям. - person AppsDev; 02.08.2013
comment
@AppsDev Хотя операции создания подклассов — очень полезная технология, с которой нужно ознакомиться в какой-то момент, если вы не хотите углубляться в тонкости связанных с этим проблем, вы также можете посмотреть AFNetworking, уважаемая сторонняя сетевая библиотека, которая (а) делает это за вас; и (б) обычно делает сетевое программирование намного проще, чем самостоятельное. - person Rob; 02.08.2013
comment
Отвечая на вопрос о преимуществах, одним из ключевых преимуществ является то, о чем я упоминал в своем ответе, а именно возможность использовать очередь, скажем, с 5 одновременными операциями, так что вы можете поставить в очередь кучу запросов и очередь операций заботится не только о параллелизме, но и о их планировании, поэтому в любой момент времени у вас не будет слишком много операций. Он также предоставляет удобный механизм для отмены операций. Очень полезно при получении тона изображений, скажем, в виде таблицы. Или много загрузок. - person Rob; 02.08.2013