Подтвердить что ты не робот

Как заставить программу ждать завершения асинхронного NSURLConnection, прежде чем продолжить следующую команду?

Как я могу заставить свою программу ждать завершения асинхронного NSURLConnection, прежде чем перейти к следующей строке кода?

SetDelegate *sjd= [SetDelegate alloc];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:post delegate:sjd];
[connection start];

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

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

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

4b9b3361

Ответ 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