Как заставить программу ждать завершения асинхронного NSURLConnection, прежде чем продолжить следующую команду?
Как я могу заставить свою программу ждать завершения асинхронного NSURLConnection, прежде чем перейти к следующей строке кода?
SetDelegate *sjd= [SetDelegate alloc];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:post delegate:sjd];
[connection start];
Вот как я запускаю соединение, и я обрабатываю данные, полученные в делегате, но я хочу дождаться окончания соединения, прежде чем продолжить, главным образом потому, что он находится в цикле for и он должен запускаться для каждого элемента в моей базе данных.
Мне нужно поместить данные из базы данных телефонов в удаленную базу данных и после того, как данные были успешно помещены в данные в базе данных телефонов, удаляется. Я просматриваю каждый элемент в базе данных телефона и запускаю соединение, поэтому я не вижу, как следующий материал можно сделать из цикла. Я начинаю, когда дело доходит до программирования objective-c, поэтому я не уверен, правильно ли это или нет.
Выполнение синхронного вызова не является опцией, поскольку оно блокирует программу, и у меня есть индикатор выполнения, который должен отображаться.
Ответы
Ответ 1
Ваш вопрос немного странный. Вы не смогли устранить проблему. У вас не может быть строки кода "wait" для завершения процесса без блокировки чего-либо, в этом случае любой поток, в котором работает цикл.
Вы можете использовать синхронный вызов, если хотите, он не блокирует ваше приложение, он только блокирует поток, в котором он выполняется. В вашем примере у вас есть цикл, который постоянно получает удаленные данные, и вы хотите, чтобы ваш пользовательский интерфейс отражал это до тех пор, пока он не будет выполнен. Но вы не хотите, чтобы ваш интерфейс заблокирован. Это означает, что этот поток с вашим циклом уже ДОЛЖЕН быть на фоновом потоке, чтобы вы могли свободно выполнять синхронный вызов в цикле без блокировки потока пользовательского интерфейса. Если цикл находится в потоке пользовательского интерфейса, вам нужно изменить его, чтобы сделать то, что вы хотите.
Вы также можете сделать это, используя асинхронное соединение. В этом случае ваша операция может быть выполнена быстрее b/c, вы можете одновременно выполнять несколько запросов. Если вы это сделаете, ваш цикл может оставаться в потоке пользовательского интерфейса, и вам просто нужно отслеживать все соединения, чтобы после их завершения вы могли передать этот статус соответствующим контроллерам. Вам понадобится iVars для отслеживания состояния загрузки и использования протокола или NSNotification для связи при загрузке.
РЕДАКТИРОВАТЬ: ДОБАВЛЕННЫЙ ПРИМЕР СИНХРОННОГО ВЫЗОВА НА ПРЕДПОСЫЛКИЮ РЕЗЬБОЙ
Если вы хотите, чтобы цикл завершался полностью, только когда все запросы заканчиваются, а не блокируйте ваш поток пользовательского интерфейса здесь, просто:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// post an NSNotification that loading has started
for (x = 0; x < numberOfRequests; x++) {
// create the NSURLRequest for this loop iteration
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
// do something with the data, response, error, etc
}
// post an NSNotification that loading is finished
});
Независимо от того, какие объекты должны показывать статус загрузки, следует наблюдать и обрабатывать уведомления, которые вы публикуете здесь. Цикл будет прерываться и делать все ваши запросы синхронно в фоновом потоке, и ваш поток пользовательского интерфейса будет разблокирован и реагировать. Это не единственный способ сделать это, и на самом деле я бы сделал это с помощью асинхронных соединений самостоятельно, но это простой пример того, как получить то, что вы хотите.
Ответ 2
Если вы просто хотите знать, когда это будет завершено и на самом деле не заботятся о каких-либо данных, просто используйте NSNotificationCenter
, чтобы опубликовать уведомление и подписаться на него.
Делегат - после уведомления
-(void) connectionDidFinishLoading:(NSURLConnection*)connection {
[[NSNotificationCenter defaultCenter] postNotificationName:@"NSURLConnectionDidFinish" object:nil];
}
Просмотр. Добавить наблюдателя и запустить код при наблюдении
-(void) viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(yourCleanupMethod)
name:@"NSURLConnectionDidFinish"
object:nil];
}
-(void) yourCleanupMethod {
// finish up
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Теперь, если вам нужно передать простой объект обратно в качестве данных, вы можете попробовать загрузить параметр объекта в своем уведомлении следующим образом:
[[NSNotificationCenter defaultCenter] postNotificationName:@"NSURLConnectionDidFinish" object:yourDataObject];
Затем измените подпись вида и очистки следующим образом:
-(void) viewDidLoad {
// Notice the addition to yourCleanupMethod
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(yourCleanupMethod:)
name:@"NSURLConnectionDidFinish"
object:nil];
}
-(void) yourCleanupMethod:(NSNotification *)notif {
// finish up
id yourDataObject = [notif object];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Теперь я нашел что-то немного больше, чем это, поэтому я закончил создание синглтона для обработки всех моих запросов. Поскольку все ваши методы делегата в NSURLConnectionDelegate
предоставляют вам и экземпляр NSURLConnection
для конкретного соединения, вы можете просто сохранить изменяемый объект данных в словаре и каждый раз просматривать его по соединению. Оттуда у меня есть сигнатура метода, которая принимает, а также объект и селектор в том, что я связываю с соединением, поэтому после того, как все завершено, я могу передать этот изменяемый объект данных запрашивающему, выполнив селектор этого объекта.
Здесь я не буду включать весь этот код, но, возможно, это поможет вам подумать о том, что возможно. Я обнаружил, что у меня было много кода, связанного с вызовом веб-сервисов, так что обертывание всего в одноэлементном режиме дало мне хороший чистый способ получения данных. Надеюсь, это поможет!
Ответ 3
Если вы действительно хотите, чтобы он подождал, зачем вообще использовать асинхронный вызов? Вместо этого используйте синхронный вызов:
NSURLResponse* response = nil;
NSData* data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:nil]
Этот подход заблокирует поток, который он выполнил, поэтому вы должны быть уверены, что хотите это сделать! Я упоминал, что он будет блокироваться?:)
Ответ 4
Вы говорите, что хотите дождаться завершения асинхронного вызова, поэтому я предполагаю, что вы вызываете код, который вы разместили в отдельном потоке.
Я бы порекомендовал взглянуть на новый метод sendAsynchronourRequest
. Я опубликовал пример того, как вы можете обернуть это в классе, который будет информировать своего делегата о завершении/завершении соединения/неудачном соединении. Я только ссылаюсь на вас на этот пост, потому что кажется, что вы пытаетесь достичь чего-то очень похожего на то, что я был в то время, и этот класс DownloadWrapper
работал безупречно для меня. Это новое в iOS5, заметьте.
Ответ 5
Этот золотой самородок помог мне!
Я просто использовал синхронный NSURL, пока не решил, что мне нужен SSL для моего соединения между моим клиентом и моим сервером. Я принял подход key pinning, который сравнивает сертификат на устройстве с сертификатом на сервере (подробнее о ссылке выше) и в порядке для этого мне нужно было добавить код в методы NSURL, которые из моих исследований вы не можете делать с NSURL синхронно.
Пока я не нашел это смехотворно простое решение, которое сработало для меня:
NSString *connectionRunLoopMode = @"connectionRunLoopMode";
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:urlRequest delegate:urlConnectionDelegate startImmediately:NO];
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
[connection unscheduleFromRunLoop:currentRunLoop forMode:NSDefaultRunLoopMode];
[connection scheduleInRunLoop:currentRunLoop forMode:connectionRunLoopMode];
[connection start];
while ([currentRunLoop runMode:connectionRunLoopMode beforeDate:[NSDate distantFuture]]);
Ответ 6
NSURLConnection уже является асинхронным. Просто реализуйте методы делегата. Если вы хотите обновить пользовательский интерфейс на MainThread (например, индикатор выполнения), вы можете сделать это в didReceiveData.
или посмотрите this