Сделайте несколько NSURLConnections и используйте метод sendAsynchronousRequest:queue:completionHandler: iOS 5

У меня есть некоторые трудности с настройкой правильной конфигурации относительно метода sendAsynchronousRequest:queue:completionHandler: (класс NSURLConnection).

Мой сценарий следующий:

Я создал одноэлементный класс, который управляет разными NSURLConnection. Этот одноэлементный экземпляр имеет NSOperation Queue (называемый downloadQueue), который делает запрос к веб-серверу и извлекает строковый путь (1). После этого путь используется для загрузки файла на веб-сервере (2). Наконец, когда файл был правильно загружен, мне нужно обновить пользовательский интерфейс (3).

Я разобрался только с первым запросом: тот, через который я могу скачать путь. Не могли бы вы предложить мне способ выполнить два других шага?

Несколько вопросов здесь:

  • очередь загрузки (downloadQueue) не является основной, можно ли в этой очереди открыть новый NSURLConnection? Другими словами, это правильно? (См. комментарии во фрагментах кода)

  • если предыдущий вопрос правильный, как я могу получить основную очередь и обновить пользовательский интерфейс?

Вот фрагмент кода, который я использую для выполнения первого шага, где downloadQueue — это переменная экземпляра, которую можно получить с помощью методов доступа (@property/@synthesized);

// initializing the queue...
downloadQueue = [[NSOperation alloc] init];    

// other code here...
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[self downloadQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

    if([data length] > 0 && error == nil) {

        // here the path (1)
        // how to perform a second connection?
        // what type of queue do I have to use?              
    }
}];

person Lorenzo B    schedule 17.04.2012    source источник


Ответы (2)


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

В блоке обработчика завершения после первой загрузки вы вычисляете URL-адрес, который вам понадобится для второй загрузки, верно? Затем вы можете выполнить вторую загрузку таким же образом: снова вызвать +[NSURLConnection sendAsynchronousRequest:...] с новым URL-адресом и той же очередью. Вы можете сделать это в блоке завершения для первой загрузки.

Чтобы обновить пользовательский интерфейс после выполнения второй загрузки, переключитесь на основную очередь в блоке завершения для этой загрузки. Вы можете сделать это с помощью dispatch_async() или dispatch_sync() (в данном случае это не имеет значения, потому что у вас нет дальнейшей работы в очереди загрузки) и dispatch_get_main_queue() или с помощью -[NSOperationQueue addOperationWithBlock:] и +[NSOperationQueue mainQueue].

Ваш код должен выглядеть примерно так:

// init download queue
downloadQueue = [[NSOperationQueue alloc] init];    

// (1) first download to determine URL for second
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[self downloadQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    if([data length] > 0 && error == nil) {
        // set newURLRequest to something you get from the data, then...
        // (2) second download
        [NSURLConnection sendAsynchronousRequest:newURLRequest queue:[self downloadQueue] completionHandler:^(NSURLResponse *newResponse, NSData *newData, NSError *newError) {
            if([newData length] > 0 && newError == nil) {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // (3) update UI
                }];
            }
        }];
    }
}];
person rickster    schedule 17.04.2012
comment
+1 за вашу поддержку. Не могли бы вы объяснить, почему я могу использовать ту же очередь для выполнения второго вызова? благодарю вас. - person Lorenzo B; 18.04.2012
comment
Это очередь: операции, которые вы помещаете в нее, выполняются в порядке «первым поступил — первым вышел». Поэтому, если вы ставите в очередь другую операцию во время выполнения первой (и после того, как основная работа — загрузка — из первой завершена), то вторая операция будет выполняться после первой. Это отвечает на ваш вопрос? (Я не уверен, что вы ищете с точки зрения того, почему это работает. Было бы легче ответить, если бы вы сказали, почему, по вашему мнению, это не сработает.) - person rickster; 18.04.2012
comment
У меня похожая проблема, разница в том, что при первой загрузке я получаю много URL-адресов, поэтому я прохожу цикл for и отправляю несколько асинхронных запросов, я пытался реализовать приведенный выше код, но он не работает. Что я делаю не так? - person Edu; 23.03.2013
comment
@Edu: опубликуйте более подробную информацию в новом вопросе, и мы посмотрим, что мы можем сделать, чтобы помочь. - person rickster; 25.03.2013
comment
NSOperationQueue является последовательным только в том случае, если для его параметра maxConcurrentOperationCount установлено значение 1. По умолчанию NSOperationQueue является настолько параллельным, насколько позволяет динамическая системная ситуация. - person MacMark; 11.04.2013

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

dispatch_async(dispatch_get_main_queue(), ^{
    //block to be run on the main thread
    [self.tableView reloadData];
});

Надеюсь, это поможет.

person swallace    schedule 17.04.2012
comment
Спасибо за ваш ответ. Вопрос, который я разместил, касается выполнения другого запроса в sendAsynchronousRequest:queue:completionHandler:. Другими словами, я хотел бы знать, как выполнить цепочку запросов, используя вышеуказанный метод. Ваше здоровье - person Lorenzo B; 18.04.2012