【问题标题】:Good pattern for Internet requests with Grand Central Dispatch?使用 Grand Central Dispatch 处理 Internet 请求的良好模式?
【发布时间】:2011-07-06 04:03:10
【问题描述】:

我目前正在使用带有 GCD 队列的同步 ASIHTTPRequest 从 Internet 下载数据,然后使用 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);
        });
    });

编辑:我使用 ASIHTTPRequest 的原因是我需要修改 OAuth 的请求标头并使用 POST 方法上传图像。

【问题讨论】:

  • 无论如何我都不是 GCD 专家,但我不明白为什么内部块需要异步执行,因为它运行在已经异步执行的外部块中。跨度>
  • @Jason,需要这样做才能在主线程上运行。
  • 我的理解是 dispatch_sync 将在您指定的不同线程(例如主线程)上执行您的块;不同的是它会阻塞当前的当前线程,直到它完成。 GCD 文档似乎表明它可能比 dispatch_async 更有效。
  • 怀疑主要的效率增益是在同一个线程上运行块的可能性。主队列的dispatch_sync 无法做到这一点,但需要设置dispatch_async 没有的同步。
  • 这也是我使用的一种模式。我很想听听为什么我应该/不应该继续使用它的任何原因。 :)

标签: objective-c cocoa asihttprequest grand-central-dispatch


【解决方案1】:

所以你替换了这个

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

来自 ASIHTTPRequest 的 10,000 多行代码

它带给你什么?

NSURLConnection 是完全异步的,使用 GCD、缓存、自动 zip/unzip 等。

因此,仅根据您提供的(可能不完整的)信息,我会说这是一段非常糟糕的代码。

当然,上下文就是一切 - 您可能有一个非常、非常、非常好的理由来重新实现 Apple 提供的库代码的现有功能。

【讨论】:

  • 你的回答很有道理,但可能有点讽刺......
  • 我使 ASIHTTPRequest 少于 6,000 行(基于 wc -l *.m) - 如果您不使用某些可选功能,则更少(尽管这不会使您的论点无效)。一个关键的区别是您的 NSURLConnection 代码在主线程上运行 json 解析,而我认为问题中的代码在不同的线程上运行它 - 如果 JSON 很大,对于避免阻塞主线程很有用。
  • @rob - 老实说,没有讽刺的意思。我相信在某些情况下 ASIHTTPRequest 真的很有用并且提供了缺失的功能。但是,(看起来)关于“使用 GCD 在后台线程上下载”或“使用异步 NSOperation 和 NSURLConnection”等方面存在很多问题。大多数“解决方案”都是过度杀伤甚至有害于NSURLConnection 的基本、标准、简单的用例。
  • @JosephH 6,000 行(可能)不必要的代码仍然很多。我承认,我也计算了一些标题。我这样做不是为了误导,这只是一个懒惰的错误。我不认为确切的行数对这一点至关重要。此外,在不同的线程上解析 json(将代码向下移动 1 行到块中并在后台线程上运行块)也很简单,而无需借助外部框架。
  • @fakeAccount22:有时异步操作很痛苦,尤其是当您有数十个 Web 请求需要使用不同的相应逻辑发出时。可以在后台运行的同步连接 (GCD) 大大简化了代码流。 NSURLConnection 缺少对同步连接的支持,但 ASIHTTPRequest 处理得很好。
【解决方案2】:

在 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];

【讨论】:

  • 所以即使这样,下载过程仍然不是基于RunLoop的。你的代码和我的代码有很大区别吗?我唯一能看到的是您使用 NSOperationQueue 而我分别使用 GCD 进行下载。好吧,在我看来,我现在更喜欢你的实现,但我不能说出原因。 :)
  • 至少,我的代码不会通过同步网络请求直接阻塞调度队列(它通过 NSOperationQueue 间接阻塞调度队列:-))。
  • 是的,我有点理解。似乎最好的方法是包装一个支持块的 NSURLConnection 类——基于 Runloop 的下载和 GCD 解析。您对此有何看法,但我需要添加大量代码以满足 OAuth 和 POST 方法实现的需要。
  • 我写了关于NSURLConnection with GCD pattern,但实际上正如你所说,你需要实现OAuth等等。 OAuthCore 怎么样?
【解决方案3】:

对于单个网络连接,该代码看起来不错,但如果您使用的是 ASIHTTPRequest,那么您很可能是一个移动应用程序。对于多个并发下载,我将实现一个队列(请参阅 ASIHTTPRequest 文档中的“使用队列”),您可以在其中指定最大同时连接的数量(比如在 GPRS 上使用 2 个,在 wifi 上使用 8 个)或限制带宽。然后,在完成选择器中使用 GDC 或其他东西在主 UI 线程之外运行数据处理。

本质上,在简单情况下使用带有 ASIHTTPRequest 的块只会为您带来关于 NSURLConnection 的不同语法,正如 fakeAccount22 所提到的。并且 NSURLConnection 也有同步方法,所以你可以避免外部依赖(以及潜在的错误/问题来源)并在块中使用它。

【讨论】:

    【解决方案4】:

    最好的方法是在获取回调时使用 gcd 而不是在发起请求时使用。然后你可以在后台线程上解析并在主线程上通知。祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 2011-06-04
      • 2018-05-14
      • 2011-07-28
      相关资源
      最近更新 更多