【问题标题】:GCD and async NSURLConnectionGCD 和异步 NSURLConnection
【发布时间】:2012-01-18 22:30:47
【问题描述】:

我知道,如果我创建一个 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 连接返回之前,如何让线程保持活动状态,或者有更好的方法可以做到这一点?

【问题讨论】:

    标签: objective-c ios grand-central-dispatch


    【解决方案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 - 您可以设置自己的线程,专门控制这些线程来管理这些连接。在设置该线程时,您可以适当地配置一个运行循环。这在 iOS4 和 5 中可以正常工作,并且显然为您提供了您想要处理的所有委托回调

    5 - 您可能会考虑异步连接处理的哪些部分真正干扰了您的 UI。通常,启动连接或接收委托回调并不那么昂贵。昂贵(或不确定)的成本通常在于您最终收集的数据的处理过程中。这里要问的问题是,您真的可以通过在某个队列上调度一个块来启动一个异步连接,该连接将立即断开并在另一个线程上执行它的操作,从而真正节省时间吗?

    因此,您可以从主线程开始连接,并在主线程上接收所有委托回调,然后在这些委托方法的实现中启动您需要的任何昂贵的工作在其他队列或线程上做。

    所以是这样的:

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

    同样,这并不全面。有很多方法可以解决这个问题,具体取决于您在此处的具体需求。但我希望这些想法有所帮助。

    【讨论】:

    • 很好的综合反应。第 5 点是我的问题 - 我优化得太早了(这不是编程罪吗?)。您还解释说,因为我没有在这个线程上配置运行循环。也感谢您指出替代方案。
    【解决方案2】:

    作为回答您的线程为何消失(以及供将来参考)的答案,NSURLConnection 需要一个运行循环。如果你添加了

    [[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,并且更现代一些)。

      【讨论】:

      • 感谢您的提示。我的 dispatch_async 不在我创建连接的位置(同意这在我的问题中并不清楚),在创建 url 连接之前,调度在调用堆栈中的位置要早得多。我认为我真正需要的是重新审视我的设计 - 因为我在不同的上下文中使用 RemoteImage 类,我需要在内部使其异步。
      【解决方案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];
                  });
              });
      

      【讨论】:

      • 我真的很喜欢这种方法。很好的答案
      猜你喜欢
      • 2011-11-17
      • 2012-09-19
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 2014-03-14
      • 1970-01-01
      • 2021-10-24
      • 1970-01-01
      相关资源
      最近更新 更多