在异步 HTTP 请求的 completionHandler 中更新视图时出现延迟

Posted

技术标签:

【中文标题】在异步 HTTP 请求的 completionHandler 中更新视图时出现延迟【英文标题】:Delay when updating view in the completionHandler of an async HTTP request 【发布时间】:2012-07-08 20:23:15 【问题描述】:

在我的应用程序中,当用户按下按钮时,我会启动 HTTP 异步请求(使用 [NSURLConnection sendAsynchronousRequest...])并更改 completionHandler 块中 UILabel 的文本。但是,此更改不会在请求结束时发生,而是在大约 2-3 秒后发生。下面是导致这种行为的代码 sn-p。

- (IBAction)requestStuff:(id)sender 

    NSURL *url = [NSURL URLWithString:@"http://***.com/"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];

    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:
     ^(NSURLResponse *response, NSData *data, NSError *error) 
     
         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;          
         exampleLabel.text = [NSString stringWithFormat:@"%d", httpResponse.statusCode];
     ];    

当我尝试在completionHandler 中创建UIAlertView 时,会发生类似的行为。

- (IBAction)requestStuff:(id)sender 

    NSURL *url = [NSURL URLWithString:@"http://***.com/"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];

    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:
     ^(NSURLResponse *response, NSData *data, NSError *error) 
     
         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; 

         if ([httpResponse statusCode] == 200) 
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"It worked!" 
                                                             message:nil
                                                            delegate:nil 
                                                   cancelButtonTitle:@"OK"
                                                   otherButtonTitles:nil];
             [alert show];
             [alert release];
         
     ];    

不过,一个小的区别是在执行[alert show] 时屏幕会变暗。警报本身仅在 2-3 秒后出现,就像之前的场景一样。

我猜这与应用程序线程如何处理 UI 有关,但我不确定。任何有关延迟发生原因的指导将不胜感激。

【问题讨论】:

【参考方案1】:

根据The Apple Docs.

线程和您的用户界面

如果您的应用程序具有图形用户界面,建议您从应用程序的主线程接收与用户相关的事件并启动界面更新。 这种方法有助于避免与处理用户事件和绘制窗口内容相关的同步问题。一些框架,例如 Cocoa,通常需要这种行为,但即使对于那些不需要这种行为的框架,将这种行为保留在主线程上也有利于简化管理用户界面的逻辑。

在主线程上调用你的 UI 更新可以解决这个问题。调用主线程(如下)围绕您的 UI 代码。

dispatch_async(dispatch_get_main_queue(), ^
   exampleLabel.text = [NSString stringWithFormat:@"%d", httpResponse.statusCode];
);

还有其他方法可以在主线程上进行调用,但使用更简单的 GCD 命令就可以完成这项工作。再次,请参阅Threaded Programming Guide 了解更多信息。

【讨论】:

【参考方案2】:

这可能是因为所有 UI 内容都应该在主队列中调用。试试这个:

- (IBAction)requestStuff:(id)sender 

    NSURL *url = [NSURL URLWithString:@"http://***.com/"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];

    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:
     ^(NSURLResponse *response, NSData *data, NSError *error) 
     
         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;          

         dispatch_async(dispatch_get_main_queue(), ^
             exampleLabel.text = [NSString stringWithFormat:@"%d", httpResponse.statusCode];
         );
     ];    

【讨论】:

【参考方案3】:

您可以尝试创建一个设置文本的方法,并在您调用的块内:

        [self performSelectorOnMainThread:@selector(mySelector) withObject:nil waitUntilDone:NO];

选择器将在主线程上调用和执行。希望对您有所帮助....

【讨论】:

以上是关于在异步 HTTP 请求的 completionHandler 中更新视图时出现延迟的主要内容,如果未能解决你的问题,请参考以下文章

多个异步 http get 请求

在异步 HTTP 请求的 completionHandler 中更新视图时出现延迟

如何在 PHP 中发出异步 HTTP 请求

在加载表单之前等待异步函数(Http 请求)

异步处理http请求同步返回结果

处理AFNetworking2.0异步HTTP请求