6

我正在尝试以编程方式创建 xls 工作表。为了填写表格,我将倍数NSURLConnection设为 100 左右。现在,我的方法是:

  1. 建立连接并将数据存储到数组中。这个数组有 100 个对象。
  2. 现在获取第一个对象并调用连接。存储数据。并与数组中的第二个对象建立第二个连接。这一直持续到数组中的最后一个对象。

完成 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 之外,还有没有更快的方法?。使用NSURLconnection和的最佳场景是NSURLConnection with asyncReq什么?

4

2 回答 2

8

几点观察:

  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实现(或 my dataTaskWithRequest)一样同时发出它们,而不是按顺序发出。这就是实现巨大性能优势的原因。

    但是请注意,您不能保证它们会完全按照您发布它们的顺序,因此您将需要使用一些支持它的结构(例如,使用NSMutableDictionary或预填充NSMutableArraywith 占位符,以便您可以简单地更新条目在特定索引处而不是向数组中添加项目)。

    最重要的是,请注意它们可能不会按照要求的顺序完成,因此请确保您处理得当。

  3. 如果您保留 100 个单独的请求,我建议您在非常慢的网络连接上进行测试(例如,使用 Network Link Conditioner 来模拟非常糟糕的网络连接;请参阅NSHipster 讨论)。只有在慢速连接时才会出现问题(超时、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在委托方法中进行所有解析。

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

于 2015-07-10T16:38:13.773 回答
1

使用sendAsynchronous是改善代码组织的好方法。我敢肯定,通过一些仔细的审查,我们可以在边际上提高速度,但显着提高速度的方法是不要发出 100 个请求。

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

如果响应主体很大,那么您请求的数据比用户目前需要的多。仅在用户需要查看的内容上保留 UI,然后静默获取其余部分(......或者,也许比静默更好,懒惰)。

如果您不控制服务器,并且响应体很小,并且用户需要全部或大部分来继续使用该应用程序,那么您可以开始在边缘处理性能和 UI 技巧以在应用程序时取悦用户有效,但通常可以放宽其中一个约束——通常是后者。

于 2015-07-10T15:04:11.900 回答