NSURLConnection sendAsynchronousRequest:阻塞主线程

The*_*ken 2 iphone asynchronous objective-c nsurlconnection ios

我正在使用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"];
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Mar*_*n R 5

while (responseRecieved == NO) {}
Run Code Online (Sandbox Code Playgroud)

你阻塞主线程(可能有几乎100%的CPU负载),直到异步块完成.然后以递归方式调用函数,启动另一个异步块并再次阻塞,直到完成为止.因此,在所有 操作完成之前,程序控制不会返回主runloop .只有这样才能完成UI更新.

您应该在完成块结束时开始下一个操作,而不是同步等待(这总是一个坏主意).

另请注意,queue参数sendAsynchronousRequest是调用完成处理程序的队列,因此您可以使用[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 ?
        }
    }];
}
Run Code Online (Sandbox Code Playgroud)

但请注意,最初的调用getRegionalInformationFromChecked现在几乎立即返回(这就是异步任务的工作原理:-).