Хороший шаблон для интернет-запросов с Grand Central Dispatch?

В настоящее время я использую синхронный ASIHTTPRequest с очередями GCD для загрузки данных из Интернета, а затем анализирую данные ответа с помощью JSONKit. Что вы думаете об этом образце. Заранее спасибо.

Вот мой код:

    dispatch_async(queue, ^(void) {

        // Request is ASIHTTPRequest.
        [request startSynchronous];

        // Parse JSON.
        NSArray *array = [[request responseData] objectFromJSONDataWithParseOptions:JKParseOptionLooseUnicode];

        // Callback on the main queue to update UI.
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            callbackBlock(array);
        });
    });

EDIT: причина, по которой я использую ASIHTTPRequest, заключается в том, что мне нужно изменить заголовок запроса для OAuth и использовать метод POST для загрузки изображений.


person nonamelive    schedule 06.07.2011    source источник
comment
Я ни в коем случае не эксперт GCD, но я не понимаю, почему внутренний блок должен выполняться асинхронно, поскольку он выполняется во внешнем блоке, который уже выполняется асинхронно.   -  person titaniumdecoy    schedule 06.07.2011
comment
@ Джейсон, это нужно сделать так, чтобы вернуться в основной поток.   -  person Steve    schedule 06.07.2011
comment
Я так понимаю, что dispatch_sync выполнит ваш блок в другом указанном вами потоке (например, в основном потоке); разница в том, что он будет блокировать текущий текущий поток, пока он не будет завершен. Документация GCD, по-видимому, указывает на то, что она может быть более эффективной, чем dispatch_async.   -  person titaniumdecoy    schedule 06.07.2011
comment
Я подозреваю, что основной выигрыш в эффективности заключается в возможности запуска блока в том же потоке. dispatch_sync в основную очередь не может этого сделать, но ему нужно будет настроить синхронизацию, которую dispatch_async не делает.   -  person Jens Ayton    schedule 06.07.2011
comment
Это шаблон, который я тоже использую. Мне было бы интересно услышать какие-либо причины, по которым я должен/не должен продолжать его использовать. :)   -  person Sedate Alien    schedule 06.07.2011
comment
@nonamelive Было бы здорово, если бы вы перечислили преимущества и недостатки этого шаблона!   -  person JosephH    schedule 06.07.2011
comment
@JosephH Ну, во-первых, этот шаблон очень прост, его очень легко поддерживать - есть только один метод для управления всем процессом загрузки-анализа-обратного вызова. Во-вторых, все операции выполняются в фоновом режиме, поэтому основной поток не будет заблокирован ни операцией загрузки, ни операцией парсинга JSON. В-третьих, использование ASIHTTPRequest упрощает запрос метода POST и поддержку OAuth.   -  person nonamelive    schedule 06.07.2011


Ответы (4)


Итак, вы заменили это

- (void)doDownload {
    NSURL *url = [NSURL URLWithString:@"http://foobar.com"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    connection = [[NSURLConnection alloc] initWithRequest:aURLRequest delegate:self];
    receivedData = [[NSMutableData data] retain];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSArray *array = [_receivedData objectFromJSONDataWithParseOptions:JKParseOptionLooseUnicode];
    callbackBlock(array);       
}

с этим -

- (void)doDownload {
    NSURL *url = [NSURL URLWithString:@"http://foobar.com"];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(queue, ^(void) {
        [request startSynchronous];
        NSArray *array = [[request responseData] objectFromJSONDataWithParseOptions:JKParseOptionLooseUnicode];

        // Callback on the main queue to update UI.
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            callbackBlock(array);
        });
    });
}

и 10 000+ строк кода из ASIHTTPRequest.

Что это у тебя есть?

NSURLConnection полностью асинхронный, использует GCD, кеши, автоматическое архивирование/распаковку и т. д. и т. д.

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

Конечно, контекст — это все, и у вас может быть очень, очень, очень веская причина для повторной реализации уже существующей функциональности кода библиотеки, предоставленной Apple.

person hooleyhoop    schedule 06.07.2011
comment
Ваш ответ вполне верен, но, возможно, немного сарказм... - person Rob Keniger; 06.07.2011
comment
Я делаю ASIHTTPRequest менее 6000 строк (на основе wc -l *.m) - меньше, если вы не используете некоторые дополнительные функции (хотя это не делает ваш аргумент недействительным). Ключевое отличие заключается в том, что ваш код NSURLConnection запускает синтаксический анализ json в основном потоке, тогда как код в вопросе запускает его в другом потоке, который, как мне кажется, полезен, чтобы избежать блокировки основного потока, если JSON большой. - person JosephH; 06.07.2011
comment
@rob - честно, без сарказма. Я считаю, что есть случаи, когда ASIHTTPRequest действительно полезен и предоставляет недостающую функциональность. Однако (похоже) на SO так много вопросов об использовании GCD для загрузки в фоновом потоке или использовании асинхронного NSOperation и NSURLConnection и т. д. Большинство «решений» являются излишними или даже вредными для основного, стандартного , простой вариант использования NSURLConnection. - person hooleyhoop; 06.07.2011
comment
@JosephH 6000 строк (возможно) ненужного кода - это все еще много. Признаюсь, я тоже посчитал несколько заголовков. Я сделал это не для того, чтобы ввести в заблуждение, это была просто ленивая ошибка. Я не считаю, что точное количество строк имеет решающее значение. Также было бы тривиально проанализировать json в другом потоке (переместить код на 1 строку вниз в блок и запустить блок в фоновом потоке), не прибегая к внешней структуре. - person hooleyhoop; 06.07.2011
comment
@fakeAccount22: Иногда асинхронные операции доставляют неудобства, особенно если у вас есть десятки веб-запросов, которые необходимо выполнить с различной соответствующей логикой. Синхронные соединения, которые можно сделать тривиальными для работы в фоновом режиме (GCD), значительно упрощают поток кода. NSURLConnection не поддерживает синхронные соединения, но ASIHTTPRequest прекрасно с этим справляется. - person Sedate Alien; 07.07.2011
comment
@MrAlien: А, я понимаю, что вы говорите, я понимаю, как это может быть удобно. Однако не думайте, что размещение нескольких синхронных операций в фоновой очереди приближает вас к поведению асинхронного URL-соединения. например, на iPhone многие загрузки, добавленные в фоновую очередь, могут загружаться последовательно, потому что GCD использует один поток на процессор, на Mac Pro это может быть 24 за раз — в обоих случаях совершенно независимо от оптимальности устройства и соединения. . - person hooleyhoop; 07.07.2011
comment
Один из негативных моментов, связанных с переводом n синхронных операций в фоновый режим, заключается в том, что они потребляют потоки, которые учитываются в пределе пула потоков GCD (неподтвержденным фактом было 64 на момент написания этой статьи) и пределе потоков для каждого процесса. Использование синхронного API для сетевого ввода-вывода на любой современной ОС — это либо недосмотр (т.е. вы не знали ничего лучше), либо лень. - person ipmcc; 02.08.2013

На WWDC2010 для сетевого программирования Apple рекомендует использовать асинхронное программирование с RunLoop.

  • WWDC 2010 Session 207 — Сетевые приложения для iPhone OS, часть 1
  • WWDC 2010 Session 208 — Сетевые приложения для iPhone OS, часть 2

Асинхронный запрос NSURLConnection является одним из наиболее эффективных способов. Но если вы хотите использовать ASIHTTPRequest, как насчет этого? (Асинхронный запрос ASIHTTPRequest реализован с использованием NSOperationQueue, это не то же самое, что асинхронный NSURLConnection.)

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        NSData *responseData = [request responseData];
        /* Parse JSON, ... */
        dispatch_async(dispatch_get_main_queue(), ^{
            callbackBlock(array);
        });
    });
}];
[request setFailedBlock:^{
    NSError *error = [request error];
    /* error handling */
}];
[request startAsynchronous];
person Kazuki Sakamoto    schedule 07.07.2011
comment
Таким образом, даже в этом случае процесс загрузки все еще не основан на RunLoop. Есть ли большая разница между вашим кодом и моим кодом. Единственное, что я вижу, это то, что вы используете NSOperationQueue, а я использую GCD для загрузки соответственно. Что ж, на мой взгляд, сейчас я предпочитаю вашу реализацию, но не могу назвать причину. :) - person nonamelive; 07.07.2011
comment
По крайней мере, мой код не блокирует напрямую очередь отправки синхронным сетевым запросом (он косвенно блокирует очередь отправки через NSOperationQueue :-)). - person Kazuki Sakamoto; 07.07.2011
comment
Да я вроде понимаю. Кажется, лучший способ - обернуть класс NSURLConnection с поддержкой блоков - загрузка на основе Runloop и разбор GCD. Что вы думаете об этом, но мне нужно добавить много кода для моих нужд реализации методов OAuth и POST. - person nonamelive; 07.07.2011
comment
Я писал о NSURLConnection с шаблоном GCD, но на самом деле, как вы сказали, вам нужно реализовать для OAuth и так далее. Как насчет OAuthCore? - person Kazuki Sakamoto; 07.07.2011

Этот код выглядит нормально для одного сетевого подключения, но если вы используете ASIHTTPRequest, вы, скорее всего, будете мобильным приложением. Для нескольких одновременных загрузок я бы реализовал очередь (см. «Использование очереди» в документах ASIHTTPRequest), где вы можете указать количество максимальных одновременных подключений (например, использовать 2 для GPRS и 8 для Wi-Fi) или ограничить пропускную способность. Затем в селекторе завершения используйте GDC или что-то еще, чтобы запустить обработку данных из основного потока пользовательского интерфейса.

По сути, использование блоков с ASIHTTPRequest для простого случая дает вам другой синтаксис в отношении NSURLConnection, как упоминалось fakeAccount22. И NSURLConnection также имеет синхронные методы, поэтому вы можете избежать внешней зависимости (и потенциального источника ошибок/проблем) и использовать ее в блоках.

person Grzegorz Adam Hankiewicz    schedule 22.07.2011

Лучший способ сделать это — использовать gcd при получении обратного вызова, а не при инициировании запроса. Затем вы можете проанализировать фоновый поток и уведомить об этом в основном потоке. Удачи!

person hfossli    schedule 30.11.2011