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

GCD и асинхронное NSURLConnection

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

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

Если я делаю

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

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


});

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

        });

    }

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

Ответ 2

Как ответ на вопрос о том, почему ваш поток был disppearing (и для будущей ссылки), NSURLConnection нуждается в runloop. Если вы добавили

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

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

Ответ 3

Во-первых, ваш блок и каждая переменная, которую вы используете в нем, будут скопированы в 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 и немного более современен).

Ответ 4

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];
            });
        });