处理多个NSURL连接的最佳方法

时间:2015-07-10 14:38:29

标签: ios objective-c nsurlconnection sendasynchronousrequest

我正在尝试以编程方式创建xls表。为了填写表单,我在100左右创建了多个NSURLConnection。现在,我的方法是:

  1. 建立连接并将数据存储到数组中。这个数组有100个对象。
  2. 现在取第一个对象并调用连接。存储数据。并在数组中与第二个对象建立第二个连接。这将持续到阵列中的最后一个对象。
  3. 完成100个连接平均需要14秒。有没有办法实现NSURLConnection以更快的方式获得响应?

    直到昨天我遵循基本方法:

    声明属性:

    @property (nonatomic,strong) NSURLConnection *getReportConnection;
    @property (retain, nonatomic) NSMutableData *receivedData;
    @property (nonatomic,strong) NSMutableArray *reportArray;
    

    viewDidLoad中初始化数组:

    reportArray=[[NSMutableArray alloc]init];
    

    在按钮操作中初始化NSURLConnection

    /initialize url that is going to be fetched.
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"****/%@/crash_reasons",ID]];
    
    //initialize a request from url
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request addValue:tokenReceived forHTTPHeaderField:@"**Token"];
    
    [request setHTTPMethod:@"GET"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    
    //initialize a connection from request
    self.getReportConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    

    处理收到的数据:

    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data{
    if (connection==_getVersionConnection) {
    
        [self.receivedData_ver appendData:data];
    
        NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
        NSError *e = nil;
        NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
    
        NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:jsonData options: NSJSONReadingMutableContainers error: &e];
        [JSON[@"app_versions"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            if (![obj[@"id"] isEqual:[NSNull null]] && ![reportArray_ver containsObject:obj[@"id"]]) {
    
                [reportArray_ver addObject:obj[@"id"]];
    
            }
            NSLog(@"index = %lu, Object For title Key = %@", (unsigned long)idx, obj[@"id"]);
        }];
    
        if (JSON!=nil) {
            UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Version Reports succesfully retrieved" message:@"" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles: nil];
            [alert show];
        }
     }
    
    }
    

    完成后再调用另一个连接:

    // This method is used to process the data after connection has made successfully.
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection{
       if (connection==getReportConnection) {
    
                 //check and call the connection again
        }
    }
    

    今天,我尝试使用NSURLConnection sendAsync使用循环依次触发所有连接,并且效果非常好。

       self.receivedData_ver=[[NSMutableData alloc]init];
    __block NSInteger outstandingRequests = [reqArray count];
     for (NSString *URL in reqArray) {
    
        NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:URL]
                                                             cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                         timeoutInterval:10.0];
    
        [request setHTTPMethod:@"GET"];
        [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    
    
    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *response,
                                               NSData *data,
                                               NSError *connectionError) {
    
                               [self.receivedData appendData:data]; //What is the use of appending NSdata into Nsmutable data? 
    
                               NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
                               NSError *e = nil;
                               NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
    
                               NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:jsonData options: NSJSONReadingMutableContainers error: &e];
                               NSLog(@"login json is %@",JSON);
    
                               [JSON[@"app_versions"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    
    
                                   if (![obj[@"id"] isEqual:[NSNull null]] && ![reportArray_ver containsObject:obj[@"id"]]) {
    
                                       [reportArray_ver addObject:obj[@"id"]];
    
                                   }
    
                                   NSLog(@"index = %lu, Object For title Key = %@", (unsigned long)idx, obj[@"id"]);
                               }];
    
    
                              outstandingRequests--;
    
                               if (outstandingRequests == 0) {
                                   //all req are finished
                                   UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Version Reports succesfully retrieved" message:@"" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles: nil];
                                   [alert show];
                               }
    
                           }];
    }
    

    这次完成100个请求所花费的时间比旧程序少一半,除了asynReq之外还有更快的方法吗?。使用NSURLconnectionNSURLConnection with asyncReq的最佳方案是什么? ?

2 个答案:

答案 0 :(得分:7)

有几点意见:

  1. 使用NSURLSession而不是NSURLConnection(如果您支持7.0及更高版本的iOS版本):

    for (NSString *URL in URLArray) {
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    
        // configure the request here
    
        // now issue the request
    
        NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            // check error and/or handle response here
        }];
        [task resume];
    }
    
  2. 如果您绝对需要发出100个请求,请与sendAsynchronousRequest实施(或我的dataTaskWithRequest)同时发布,而不是按顺序发布。这是实现巨大性能优势的原因。

    但请注意,您无法保证它们完全符合您发布它们的顺序,因此您需要使用一些支持它的结构(例如使用NSMutableDictionary或预先填充带有占位符的NSMutableArray因此您只需更新特定索引处的条目,而不是将项添加到数组中。

    最重要的是,请注意它们可能无法按照要求完成相同的顺序,因此请确保妥善处理。

  3. 如果您保留100个单独的请求,我建议您在非常慢的网络连接上进行测试(例如,使用网络链接调节器来模拟真正糟糕的网络连接;请参阅NSHipster discussion) 。只有在慢速连接时才会出现问题(超时,UI打嗝等)。

  4. 我建议使用调度组或操作队列依赖项,而不是递减待处理请求数的计数器。

    dispatch_group_t group = dispatch_group_create();
    
    for (NSString *URL in URLArray) {
        dispatch_group_enter(group);
    
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    
        // configure the request here
    
        // now issue the request
    
        NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            // check error and/or handle response here
    
            // when all done, leave group
    
            dispatch_group_leave(group);
        }];
        [task resume];
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // do whatever you want when all of the requests are done
    });
    
  5. 如果可能,请查看您是否可以重构Web服务,以便发出一个返回所有数据的请求。如果您正在寻找进一步的性能改进,那可能就是这样做的方式(并且它避免了在发出100个单独的请求时涉及到很多复杂性)。

  6. 顺便说一句,如果你使用基于委托的连接,就像你在原始问题中所做的那样,你应该解析didReceiveData中的数据。这应该只是将数据附加到NSMutableData。在connectionDidFinishLoading委托方法中进行所有解析。

    如果你去基于块的实现,这个问题就会消失,但只是观察你的代码片段。

答案 1 :(得分:1)

使用sendAsynchronous是改善代码组织的好方法。我确信经过仔细审查,我们可以提高边际速度,但显着提高速度的方法是不要提出100个请求。

如果响应主体很小,请创建一个回答结果组合的端点。

如果响应主体很大,那么您现在需要的数据超出用户需要的数据。仅根据用户需要查看的内容来保持UI,并以静默方式获取其余内容(...或者,可能比默默地懒得更好)。

如果您不控制服务器,并且响应机构很小,并且用户需要全部或大部分时间来继续使用该应用程序,那么您可以开始处理边距和UI技巧的性能在应用程序运行时逗乐用户,但通常可以放松其中一个限制 - 通常是后者 -