GCD и асинхронный NSURLConnection - PullRequest
24 голосов
/ 19 января 2012

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

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

Если я сделаю

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    /* and inside here, at some NSURLConnection is created */


});

.. возможно ли, что мое NSURLConnection создано, но мой поток исчезает до того, как соединение URL вернулось?

Я новичок в GCD.Как сохранить поток живым до тех пор, пока не вернется мое соединение с URL, или есть лучший способ сделать это?

Ответы [ 4 ]

37 голосов
/ 19 января 2012

Таким образом, на самом деле проблема не в времени жизни потока, в котором работает ваш блок, а в том, что этому конкретному потоку не будет настроен и запущен цикл запуска для получения каких-либо событий, возвращающихся из соединения.

Так как вы решаете это?Есть разные варианты для размышления.Я могу перечислить несколько, и я уверен, что другие перечислят больше.

1 - Вы можете использовать синхронное соединение здесь.Одним из недостатков является то, что вы не получите обратные вызовы для аутентификации, перенаправления, кэширования и т. Д. (Все обычные недостатки синхронных соединений.) Кроме того, каждое соединение, конечно, будет блокировать поток на некоторый период времени, так что если вы делаетеМногие из них могут потенциально заблокировать несколько потоков одновременно, что дорого.

2 - если ваше соединение простое и вы используете iOS5, вы можете использовать этот метод:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request
                          queue:(NSOperationQueue*) queue
              completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))

Это запустит асинхронное соединение, а затем позволит вам указать обработчик завершения (для успеха или неудачи) и NSOperationQueue, для которого вы хотите, чтобы этот блок был запланирован.

Опять же, у вас есть недостатки в том, что вы не можете получить обратные вызовы, которые вам могут понадобиться для аутентификации, кэширования и т. Д. Но, по крайней мере, у вас нет зависающих потоков, заблокированных соединениями, которые находятся в полете.1019 * 3 - Другой вариант для iOS5 - установить очередь для всех обратных вызовов делегата :

- (void)setDelegateQueue:(NSOperationQueue*) queue NS_AVAILABLE(10_7, 5_0);

Если вы используете это, то все методы делегата будут выполняться в контексте любой указанной вами NSOperationQueue.Так что это похоже на вариант # 2, ожидайте, что вы получите все методы делегата для обработки аутентификации, перенаправления и т. Д.

4 - вы можете настроить свой собственный поток, который выконтролировать специально для управления этими соединениями.И при настройке этого потока вы настраиваете runloop соответствующим образом.Это хорошо работает в iOS4 и 5 и, очевидно, дает вам все обратные вызовы делегатов, которые вы хотите обработать

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

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

Так что-то вроде этого:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {      
        // go ahead and receive this message on the main thread
        // but then turn around and fire off a block to do the real expensive work

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

         // Parse the data we've been collecting

        });

    }

Опять же, это не является исчерпывающим.Есть много способов справиться с этим, в зависимости от ваших конкретных потребностей здесь.Но я надеюсь, что эти мысли помогут.

9 голосов
/ 26 мая 2012

Точно так же, как ответ на вопрос, почему ваш поток отображал (и для дальнейшего использования), NSURLConnection нуждается в цикле выполнения. Если бы вы добавили

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];

Вы увидите, что соединение работает правильно, и поток не исчезает, пока соединение не будет установлено.

3 голосов
/ 19 января 2012

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

Если вы хотите вернуть свои данные в основной поток, вы можете вложить асинхронный вызов после получения ваших данных:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"www.stackoverflow.com"]];
    NSURLResponse *response;
    NSError *error;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    if (error) {
        // handle error
        return;
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        // do something with the data
    });
});

Но почему бы не использовать встроенную асинхронную поддержку NSURLConnection? Вам нужно NSOperationQueue, но если вы делаете много сетевых извлечений, то это путь в любом случае:

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"www.stackoverflow.com"]];
[NSURLConnection sendAsynchronousRequest:request
                                   queue:self.queue // created at class init
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
                           // do something with data or handle error
                       }];

Лично я использую такие библиотеки, как AFNetworking или ASIHTTPRequest, чтобы упростить работу в сети, причем оба поддерживают блоки (первый использует GCD и немного более современный).

0 голосов
/ 22 октября 2013
dispatch_queue_t queue = dispatch_get_global_queue(  DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{
        [btnCreateSmartList setEnabled:NO];
        [dbSingleton() createEditableCopyOfDatabaseIfNeeded];
        [dbSingleton() insert_SMART_PlaceList:txtListName.text :0:txtTravelType.text:          [strgDuration intValue]:strgTemprature:Strgender:bimgdt];
        [self Save_items];
//*********navigate new
            dispatch_async(dispatch_get_main_queue(), ^{
                [activityIndicator stopAnimating];
                [self performSelector:@selector(gonext_screen) withObject:nil afterDelay:0.0];
            });
        });
...