2013-05-17 2 views
1

Это было трудно найти. Я нашел аналогичный вопрос, iOS 5 Wait for delegate to finish before populating a table?, но принятый ответ был «Refresh the table view», и это мне не помогает. Другие результаты, которые я нашел, как правило, находятся в C#.Как дождаться завершения делегата NSURLConnection перед выполнением следующего оператора?

У меня есть приложение, которое транслируется с iPhone на серверы Wowza. Когда пользователь нажимает на запись, я генерирую уникальный идентификатор устройства, а затем отправляю его на скрипт PHP на сервере, который возвращает документ JSON с настройками конфигурации (который включает ссылку dump rmmp).

Проблема заключается в том, что методы делегата являются асинхронными, но мне нужно получить настройки конфигурации перед следующими строками кода в моем методе - (IBAction)recordButtonPressed, так как этот код устанавливает настройки профиля, а затем записывается на основе этих настроек ,

Я понял, что я мог сделать NSURLConnection в -recordButtonPressed как я в настоящее время, а затем продолжить код установки внутри метод делегата connectionDidFinishLoading (или просто инкапсулировать установка и метод называют его оттуда), но это жертвуя когерентное дизайн для функциональности и отстой.

Есть ли какой-то простой флаг waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag, который я могу отправить делегату, чтобы я мог выполнять последовательные операции, которые извлекают данные из Интернета?

+1

ли вы имеете в виду вы хотите Синхронный способ сделать загрузку? – Wain

+0

Загрузка уже обработана. Мне просто нужен синхронный способ захвата настроек конфигурации из сценария php перед началом записи. – user

+1

Никогда не ждите в потоке пользовательского интерфейса. –

ответ

1

Я понял, что я мог сделать NSURLConnection в -recordButtonPressed, как я в настоящее время, а затем продолжить код установки внутри метода делегата connectionDidFinishLoading (или просто инкапсулировать установка и метод называют его оттуда), но это пожертвовав когерентный дизайн для функциональности и отстой.

Вы проанализировали и поняли ситуацию, и вы прекрасно описали ее возможные решения. Я просто не согласен с вашими выводами. Такого рода вещи происходят все время:

- (void) doPart1 { 
    // do something here that will eventually cause part2 to be called 
} 

- (void) doPart2 { 
} 

Вы можете играть в различные игры с заклинаниями, чтобы сделать это более элегантный и универсальный, но мой совет был бы, не боритесь рамки, как то, что вы описываете является именно тем, что является асинхронным. (И do не используют синхронный запрос на основную резьбу, так как это блокирует основную резьбу, которая не имеет значения.)

Действительно, в фреймворке, основанном на событиях, само понятие «дождитесь», является анафемой.

+0

Это * действительно * помогает. Так вы скажете, что явное ожидание в iOS никогда не требуется? Или у него есть свои цели? – user

+1

Я бы сказал, что явное ожидание * на основной теме * никогда не является необходимым или законным. Это среда, управляемая событиями. Ваша задача состоит в том, чтобы подготовиться к участию в мероприятиях, спросить о получении событий, организовать прием событий, но это все о получении событий, порядок и время которых вы не контролируете. Целая глава моей книги посвящена попытке заставить вас подумать о том, как вам нужно думать в рамках структуры: http://www.apeth.com/iOSBook/ch11.html – matt

+1

И в конкретном случае, который вы описываете, Методы NSURLConnectionDelegate, такие как 'connectionDidFinishLoading:' * *, как вы «дождитесь». Когда и если соединение будет завершено, вы получите событие. Тогда вы отвечаете. Вы не сидите, сжимая большие пальцы; фреймворк делает ожидание на фоновом потоке для вас. – matt

0

Почему не использовать synchronous request?

+2

Никогда не используйте синхронный запрос на основную резьбу. И если он делает это в фоновом потоке, мы возвращаемся к одной и той же ситуации - он должен иметь возможность повторно войти во вторую часть своей конфигурации. – matt

0

Оберните асинхронный запрос NSURLConnection в вспомогательный метод, который имеет блок завершения в качестве параметра:

-(void) asyncDoSomething:(void(^)(id result)completionHandler ;

Этот метод должен быть реализован в NSURLConnectionDelegate. Подробнее см. Пример реализации и комментарии ниже.

В другом месте, в методе действия:

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

- (IBAction) recordButtonPressed 
{ 
    [someController asyncConnectionRequst:^(id result){ 
     if (![result isKindOfClass:[NSError class]]) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       // We are on the main thread! 
       someController.tableData = result; 
      }); 
     }  
    }]; 
} 

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

Но есть лучший способ: блок-оболочка будет немедленно отправлен в приостановленный последовательной очереди отправки - который удерживается как ivar. Поскольку очередь приостановлена, они не будут выполнять никаких блоков. Только до тех пор, пока очередь не будет возобновлена, блок будет выполняться. Возобновление очереди в вашем connectionDidFinish: и connectionDidFailWithError: (см):

В вашем NSURLConnectionDelegate:

-(void) asyncConnectionRequst:(void(^)(id result)completionHandler 
{ 
    // Setup and start the connection: 
    self.connection = ... 
    if (!self.connection) { 
     NSError* error = [[NSError alloc] initWithDomain:@"Me" 
          code:-1234 
         userInfo:@{NSLocalizedDescriptionKey: @"Could not create NSURLConnection"}]; 
      completionHandler(error); 
     }); 
     return; 
    } 
    dispatch_suspend(self.handlerQueue); // a serial dispatch queue, now suspended 
    dispatch_async(self.handlerQueue, ^{ 
     completionHandler(self.result); 
    }); 
    [self.connection start]; 
} 

Тогда в NSURLConnectionDelegate, направить обработчик и резюме очереди обработчика:

- (void) connectionDidFinishLoading:(NSURLConnection*)connection { 
    self.result = self.responseData; 
    dispatch_resume(self.handlerQueue); 
    dispatch_release(_handlerQueue), _handlerQueue = NULL; 
} 

Аналогично, когда произошла ошибка:

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{ 
    self.result = error; 
    dispatch_resume(self.handlerQueue); 
    dispatch_release(_handlerQueue), _handlerQueue = NULL; 
} 

Есть еще более эффективные способы, которые, однако, включать в себя несколько основных вспомогательных классов, которые имеют дело с асинхронными архитектурами, которые в конце дня сделать вашу асинхронную код посмотреть, как это было синхронными:

-(void) doFourTasksInAChainWith:(id)input 
{ 
    // This runs completely asynchronous! 

    self.promise = [self asyncWith:input] 
    .then(^(id result1){return [self auth:result1]);}, nil) 
    .then(^(id result2){return [self fetch:result2];}, nil) 
    .then(^(id result3){return [self parse:result3];}, nil) 
    .then(^(id result){ self.tableView.data = result; return nil;}, ^id(NSError* error){ ... }) 

    // later eventually, self.promise.get should contain the final result   
} 
Смежные вопросы