【问题标题】:NSURLConnection sendAsynchronousRequest: Blocking Main ThreadNSURLConnection sendAsynchronousRequest:阻塞主线程
【发布时间】:2013-04-20 19:36:39
【问题描述】:

我正在使用NSURLConnection 发出多个异步请求。我想显示一个进度指示器,以显示在要执行的总数中完成了多少请求。但是,当我尝试在发出请求之前或在执行请求之前调用的另一个方法中设置和显示此进度指示器时,它不会显示。当请求被注释掉时,进度指示器显示正常。但如果不是这样,就好像 Xcode 向前看并看到一个异步请求即将到来并阻塞主线程,从而使 UI 更改变得不可能。

这是被调用的相关代码,包括请求和显示进度指示器的代码:

- (void)getRegionalInformationFromChecked:(NSSet *)set atIndex:(NSInteger)index {
    __block BOOL responseRecieved = NO;
    NSString *stringForURL = [NSString stringWithFormat:@"http://www.thebluealliance.com/api/v1/event/details?event=%@",[[set allObjects] objectAtIndex:index]];
    NSURL *url = [NSURL URLWithString:stringForURL];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSLog(@"URL IS GO: %@", stringForURL);
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:queue completionHandler:^(NSURLResponse *_response, NSData *_data, NSError *_error) {
    NSLog(@"CHECKED DATA RETURNED AT INDEX %i", index);
    NSError *error;
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:&error];
    if (!_regionalDetails) {
        _regionalDetails = [[NSMutableArray alloc] init];
    }
    [_regionalDetails addObject:dict];
    responseRecieved = YES;
}];
    regionalSchedulesToGet = [set count];
    while (responseRecieved == NO) {}
    [[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:[NSString stringWithFormat: @"Getting regional %i of %i", index+2, [set count]]];
    if (index+1 < [set count]) {
        [self getRegionalInformationFromChecked:set atIndex:index+1];
    } else {
        [[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:@"Writing to file"];
    }
}

当异步请求的块被注释掉时,MBProgressHUD 会很好地显示它的值。但是当插入块时,SDK 拒绝更新进度指示器,即使在离开块之后(之后应该已经解决了任何线程问题)。在没有更多请求显示之前它不会更新,此时它会显示“正在写入文件”。

为什么异步请求似乎阻塞了主线程,为什么我不能在调用请求之前或之后立即对主线程进行更改?

【问题讨论】:

  • 它不会阻塞线程。这是您的程序调用 UB,因为您没有从主线程调用 UIKit。
  • 嗯。我还尝试使用 dispatch_sync() 获取主队列并以这种方式更新 UI,但无济于事。

标签: iphone ios objective-c asynchronous nsurlconnection


【解决方案1】:

while (responseRecieved == NO) {}

您阻塞主线程(可能几乎 100% 的 CPU 负载)直到异步块完成。然后你打电话给你的 递归函数,启动另一个异步块并再次阻塞,直到有 完成的。因此程序控制直到 all 才会返回到主 runloop 操作已经完成。只有这样 UI 更新才完成。

而不是同步等待(这总是一个坏主意), 您应该在完成块的末尾开始下一个操作。

还要注意sendAsynchronousRequestqueue 参数是其所在的队列 完成处理程序被调用,所以你可以使用[NSOperationQueue mainQueue]

那么你的代码大概是这样的:

- (void)getRegionalInformationFromChecked:(NSSet *)set atIndex:(NSInteger)index
{
    [[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]]
     setLabelText:[NSString stringWithFormat:@"Getting regional %i of %i", index+1, [set count]]];
    NSString *stringForURL = [NSString stringWithFormat:@"http://www.thebluealliance.com/api/v1/event/details?event=%@",[[set allObjects] objectAtIndex:index]];
    NSURL *url = [NSURL URLWithString:stringForURL];
    NSLog(@"URL IS GO: %@", stringForURL);
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *_response, NSData *_data, NSError *_error) {
        NSLog(@"CHECKED DATA RETURNED AT INDEX %i", index);
        NSError *error;
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:&error];
        if (!_regionalDetails) {
            _regionalDetails = [[NSMutableArray alloc] init];
        }
        [_regionalDetails addObject:dict];
        if (index+1 < [set count]) {
            [self getRegionalInformationFromChecked:set atIndex:index+1];
        } else {
            [[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:@"Writing to file"];
            // ... perhaps call a completion function from here ?
        }
    }];
}

但请注意,对getRegionalInformationFromChecked 的初始调用现在将 几乎立即返回(这就是异步任务的工作方式:-)。

【讨论】:

  • 非常感谢。一个简短的问题——如果我内置了 while 循环来等待收到响应,那岂不是避免了请求相互堆积和控制被主线程保留的问题?
  • @TheKraken:你说的“内置的while循环等待......”是什么意思?? - 但无论如何,即使请求没有堆积起来,只要你在主线程上同步“等待”,不会进行任何 UI 更新,从而导致应用无响应,因此应始终避免这种情况。跨度>
【解决方案2】:

尝试在主线程上调度所有涉及 UI 刷新的方法

【讨论】:

  • 你的意思是使用 GCD 吗?我试过了。没有区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-16
  • 1970-01-01
  • 2015-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多