处理多个 NSURL 连接的最佳方式
Best way to handle multiple NSURL connections
我正在尝试以编程方式创建 xls sheet。为了填充 sheet,我将倍数 NSURLConnection
设置为 100 左右。现在,我的方法是:
- 建立连接并将数据存储到数组中。这个数组有 100 个对象。
- 现在获取第一个对象并调用连接。存储数据。并与数组中的第二个对象建立第二个连接。这一直持续到数组中的最后一个对象。
完成 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
和 [=23 的最佳方案是什么=]?
使用 sendAsynchronous
是改进代码组织的好方法。我敢肯定,经过一些仔细的审查,我们可以稍微提高速度,但显着提高速度的方法是不要发出 100 个请求。
如果响应主体很小,请创建一个端点来回答结果的结合。
如果响应正文很大,那么您请求的数据比用户目前需要的多。仅在用户需要查看的内容上保留 UI,然后默默地获取其余部分(...或者,也许比默默地懒惰地更好)。
如果您不控制服务器,并且响应体很小,并且用户需要全部或大部分来继续使用该应用程序,那么您可以开始在边缘和 UI 在应用程序运行时取悦用户的技巧,但通常这些限制之一(通常是后者)可以放松。
一些观察:
使用 NSURLSession
而不是 NSURLConnection
(如果您支持 iOS 7.0 及更高版本):
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];
}
如果您绝对必须发出 100 个请求,那么请像您的 sendAsynchronousRequest
实施(或我的 dataTaskWithRequest
)那样同时发出它们,而不是按顺序发出。这就是实现巨大性能优势的原因。
但是请注意,您无法保证它们会完全按照您发布它们的顺序进行,因此您需要使用一些支持它的结构(例如使用 NSMutableDictionary
或预填充NSMutableArray
带有占位符,因此您可以简单地更新特定索引处的条目,而不是将项目添加到数组中)。
最重要的是,请注意它们可能不会按照要求的顺序完成,因此请确保您妥善处理。
如果您保留 100 个单独的请求,我建议您在非常慢的网络连接上测试它(例如,使用网络 Link 调节器来模拟非常糟糕的网络连接;请参阅NSHipster discussion)。有些问题(超时、UI 打嗝等)只有在连接速度较慢时才会出现。
我建议使用分派组或操作队列依赖项,而不是递减挂起请求数的计数器。
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
});
如果可能,看看您是否可以重构 Web 服务,以便发出一个 returns 所有数据的请求。如果您正在寻找进一步的性能改进,这可能是实现它的方法(并且它避免了发出 100 个单独请求时涉及的很多复杂性)。
顺便说一句,如果您使用基于委托的连接,就像您在原始问题中所做的那样,您应该 而不是 解析 didReceiveData
中的数据。那应该只是将数据附加到 NSMutableData
。在 connectionDidFinishLoading
委托方法中进行所有解析。
如果您转到基于块的实现,这个问题就会消失,但只是对您的代码片段的观察。
我正在尝试以编程方式创建 xls sheet。为了填充 sheet,我将倍数 NSURLConnection
设置为 100 左右。现在,我的方法是:
- 建立连接并将数据存储到数组中。这个数组有 100 个对象。
- 现在获取第一个对象并调用连接。存储数据。并与数组中的第二个对象建立第二个连接。这一直持续到数组中的最后一个对象。
完成 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
和 [=23 的最佳方案是什么=]?
使用 sendAsynchronous
是改进代码组织的好方法。我敢肯定,经过一些仔细的审查,我们可以稍微提高速度,但显着提高速度的方法是不要发出 100 个请求。
如果响应主体很小,请创建一个端点来回答结果的结合。
如果响应正文很大,那么您请求的数据比用户目前需要的多。仅在用户需要查看的内容上保留 UI,然后默默地获取其余部分(...或者,也许比默默地懒惰地更好)。
如果您不控制服务器,并且响应体很小,并且用户需要全部或大部分来继续使用该应用程序,那么您可以开始在边缘和 UI 在应用程序运行时取悦用户的技巧,但通常这些限制之一(通常是后者)可以放松。
一些观察:
使用
NSURLSession
而不是NSURLConnection
(如果您支持 iOS 7.0 及更高版本):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]; }
如果您绝对必须发出 100 个请求,那么请像您的
sendAsynchronousRequest
实施(或我的dataTaskWithRequest
)那样同时发出它们,而不是按顺序发出。这就是实现巨大性能优势的原因。但是请注意,您无法保证它们会完全按照您发布它们的顺序进行,因此您需要使用一些支持它的结构(例如使用
NSMutableDictionary
或预填充NSMutableArray
带有占位符,因此您可以简单地更新特定索引处的条目,而不是将项目添加到数组中)。最重要的是,请注意它们可能不会按照要求的顺序完成,因此请确保您妥善处理。
如果您保留 100 个单独的请求,我建议您在非常慢的网络连接上测试它(例如,使用网络 Link 调节器来模拟非常糟糕的网络连接;请参阅NSHipster discussion)。有些问题(超时、UI 打嗝等)只有在连接速度较慢时才会出现。
我建议使用分派组或操作队列依赖项,而不是递减挂起请求数的计数器。
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 });
如果可能,看看您是否可以重构 Web 服务,以便发出一个 returns 所有数据的请求。如果您正在寻找进一步的性能改进,这可能是实现它的方法(并且它避免了发出 100 个单独请求时涉及的很多复杂性)。
顺便说一句,如果您使用基于委托的连接,就像您在原始问题中所做的那样,您应该 而不是 解析
didReceiveData
中的数据。那应该只是将数据附加到NSMutableData
。在connectionDidFinishLoading
委托方法中进行所有解析。如果您转到基于块的实现,这个问题就会消失,但只是对您的代码片段的观察。