如何知道NSURLSessionDataTasks的for循环何时完成

sli*_*c07 4 loops objective-c objective-c-blocks ios7 nsurlsession

我正在调用一个方法,它将枚举数组,创建一个NSURL,并调用一个返回JSON的NSURLSessionDataTask.循环通常运行大约10次,但可以根据日期而变化.

在开始处理数据之前,我需要等待for循环和所有NSURLSessionDataTasks完成.

我很难搞清楚所有工作何时完成.任何人都可以推荐任何方法或逻辑来了解整个方法何时完成(对于循环和数据任务)?

-(void)findStationsByRoute{
for (NSString *stopID in self.allRoutes) {
    NSString *urlString =[NSString stringWithFormat:@"http://truetime.csta.com/developer/api/v1/stopsbyroute?route=%@", stopID];
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request
                                                 completionHandler:^(NSData *data,
                                                                     NSURLResponse *response,
                                                                     NSError *error) {
                                                     NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                                                     if(httpResponse.statusCode == 200){
                                                         NSError *jsonError = [[NSError alloc]init];
                                                         NSDictionary *stopLocationDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
                                                         NSArray *stopDirectionArray = [stopLocationDictionary objectForKey:@"direction"];
                                                         for (NSDictionary * _stopDictionary in stopDirectionArray) {
                                                             NSArray *stop =   [_stopDictionary objectForKey:@"stop"];
                                                             [self.arrayOfStops addObject:stop];

                                                         }
                                                     }

                                                 }];

    [task resume];
}
Run Code Online (Sandbox Code Playgroud)

}

Rob*_*Rob 5

有很多选择.基本问题是这些单独的数据任务是异步运行的,因此您需要一些方法来跟踪这些异步任务并建立对它们完成的依赖.

有几种可能的方法:

  1. 典型的解决方案是使用调度组.在启动请求之前输入组dispatch_group_enter,将组保留在dispatch_group_leave完成处理程序内,该处理程序是异步调用的,然后在循环结束时提供一个dispatch_group_notify在所有"enter"调用时异步调用的块由相应的"离开"调用抵消:

    - (void)findStationsByRoute {
        dispatch_group_t group = dispatch_group_create();
    
        for (NSString *stopID in self.allRoutes) {
            NSString     *urlString = [NSString stringWithFormat:@"http://truetime.csta.com/developer/api/v1/stopsbyroute?route=%@", stopID];
            NSURL        *url       = [NSURL URLWithString:urlString];
            NSURLRequest *request   = [NSURLRequest requestWithURL:url];
    
            dispatch_group_enter(group);   // enter group before making request
    
            NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                if(httpResponse.statusCode == 200){
                    NSError *jsonError;   // Note, do not initialize this with [[NSError alloc]init];
                    NSDictionary *stopLocationDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
                    NSArray *stopDirectionArray = [stopLocationDictionary objectForKey:@"direction"];
                    for (NSDictionary *stopDictionary in stopDirectionArray) {
                        NSArray *stop = [stopDictionary objectForKey:@"stop"];
                        [self.arrayOfStops addObject:stop];
                    }
                }
    
                dispatch_group_leave(group);  // leave group from within the completion handler
            }];
    
            [task resume];
        }
    
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            // do something when they're all done
        });
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 处理此问题的一种更复杂的方法是将子包装NSSessionDataTaskNSOperation子类中,然后可以使用数据任务操作和最终完成操作之间的依赖关系.您需要确保您的单个数据任务操作是"并发"操作(即isFinished在异步数据任务完成之前不发出通知).这种方法的好处是您可以设置maxConcurrentOperationCount约束在任何给定时间将启动的请求数.通常,您希望一次将其约束为3-4个请求.

    请注意,这也可以解决调度组方法可能遇到的超时问题.调度组不会限制在任何给定时间提交的请求数量,而这很容易实现NSOperation.

    有关更多信息,请参阅"并发编程指南 "的" 操作队列"部分中有关"并发操作"的讨论.

    有关NSURLSessionTask在异步NSOperation子类中包装请求的示例,请参阅后一半NSURLSession与NSBlockOperation和队列的简单实现.这个问题是针对不同的主题,但最后我包含了一个NSOperation子类示例.

  3. 如果不是数据任务,您使用的上传/下载任务,然后你可以使用一个[NSURLSessionConfiguration backgroundSessionConfiguration]URLSessionDidFinishEventsForBackgroundURLSession:NSURLSessionDelegate当所有的任务都做了和应用程序被带回到前台会再被调用.(有点烦人的是,只有当你的应用程序在下载完成后才处于活动状态时才会调用此选项:我希望即使应用程序在下载完成时位于前台,也会调用此委托方法.)

    当您询问数据任务(不能与后台会话一起使用)时,使用具有上传/下载任务的后台会话享有后台操作的显着优势.如果您的过程确实需要10分钟(这看起来非同寻常),那么为后台会话重构这一过程可能会带来显着的优势.

  4. 我讨厌甚至提到这一点,但为了完整起见,我应该承认理论上你可以通过维护一个可变数组或待处理数据任务字典,并在完成每个数据任务后,从该列表中删除一个项目,并且,如果它结束它是最后一个任务,则手动启动完成过程.