我正在为 iOS 应用程序编写一个网络类。此类将处理所有日志记录和网络流量。我遇到一个问题,我必须一次发送可能数千个请求,但 NSURLConnections 超时,因为在所有 NSURLConnections 启动之前不会调用委托(delegate)方法,此时超时期限已到期。我正在使用 Drupal 的 REST API,不幸的是,我不知道如何通过一个请求创建多个实例。如何在发送回复的同时接收回复?如果我使用 GCD 来传递 NSURLConnections 的创建,可以解决问题吗?我想我必须传递迭代要发送的对象并将其发送到 GCD 的整个操作,以释放主线程来响应响应。
-(BOOL)sendOperation:(NetworkOperation)op
NetworkDataType:(NetworkDataType)dataType
JsonToSend:(NSArray *)json
BackupData:(NSArray *)data
{
if(loggingMode)
{
return YES;
}
NSURLConnection *networkConnection;
NSData *send;
NSString *uuid = [self generateUUID];
NSMutableArray *connections = [[NSMutableArray alloc] init];
NSMutableURLRequest *networkRequest;
for (int i=0; i<[json count] && (data ? i<[data count] : YES); i++)
{
if(op == Login)
{
/*Grab all cookies from the server domain and delete them, this prevents login failure
because user was already logged in. Probably find a better solution like recovering
from the error*/
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:
[[NSURL alloc] initWithString:networkServerAddress]];
for (NSHTTPCookie *cookie in cookies)
{
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
networkRequest = [[NSMutableURLRequest alloc] initWithURL:
[NSURL URLWithString:[networkServerAddress stringByAppendingString:@"/user/login"]]];
}
else if(op == StartExperiment)
{
networkRequest = [[NSMutableURLRequest alloc] initWithURL:
[NSURL URLWithString:[networkServerAddress stringByAppendingString:@"/node"]]];
}
else if(op == Event || op == EndExperiment || op == SendAll)
{
networkRequest = [[NSMutableURLRequest alloc] initWithURL:
[NSURL URLWithString:[networkServerAddress stringByAppendingString:@"/node"]]];
}
else if(op == Logout)
{
networkRequest = [[NSMutableURLRequest alloc] initWithURL:
[NSURL URLWithString:[networkServerAddress stringByAppendingString:@"/user/logout"]]];
}
send = [[json objectAtIndex:i] dataUsingEncoding:NSUTF8StringEncoding];
//Set the headers appropriately
[networkRequest setHTTPMethod:@"POST"];
[networkRequest setValue:@"application/json"
forHTTPHeaderField: @"Content-type"];
[networkRequest setValue:[NSString stringWithFormat:@"%d", [send length]]
forHTTPHeaderField:@"Content-length"];
[networkRequest setValue:@"application/json"
forHTTPHeaderField:@"Accept"];
//Set the body to the json encoded string
[networkRequest setHTTPBody:send];
//Starts async request
networkConnection = [[NSURLConnection alloc] initWithRequest:networkRequest delegate:self];
//Successfully created, we are off
if(networkConnection)
{
[networkConnectionsAndData setValue:[[NSMutableArray alloc] initWithObjects:uuid,
[[NSNumber alloc] initWithInt:op], [[NSNumber alloc] initWithInt:dataType], [[NSMutableData alloc] init], (data ? [data objectAtIndex:i] : [NSNull null]), nil]
forKey:[networkConnection description]];
}
else //Failed to conn ect
{
NSLog(@"Failed to create NSURLConnection");
return NO;
}
}
[[self networkOperationAndConnections] setObject:[[NSMutableDictionary alloc] initWithObjectsAndKeys:[[NSMutableArray alloc] initWithObjects:connections, nil], @"connections", [[NSMutableArray alloc] init], @"errors", nil]
forKey:uuid];
return YES;
}
字典用于跟踪每个 NSURLConnection 的相关数据,并将 NSURLConnection 分组为一组以确定整个操作的最终成功或失败。
更新
AFNetworking 是完成该项目的关键。它不仅大大清理了代码,而且还处理了发送如此多请求时继承的所有线程问题。更不用说使用 AFNetworking,我可以将所有请求一起批处理到一个操作中。使用 block (如 AFNetworking 使用的那样)是比 NSURLConnections 的标准委托(delegate)更干净、更好的解决方案。
最佳答案
您肯定需要允许 NSURLRequest/Connection 在另一个线程上操作。 (不是主线程!)
为清楚起见进行了编辑**:
我注意到您对“//Starts async request
”的评论,我想确保您意识到您的调用并不是您所期望的典型“异步”函数。实际上,它只是同步触发请求,但由于它是一个 Web 请求,它本质上是异步行为的。您实际上希望将这些请求放在另一个线程上以实现完全异步行为。
抛开其他一切不谈,我真的建议在这里深入研究 Apple 的网络示例项目:MVCNetworking
至于您问题的具体情况,有几种方法可以做到这一点。
- 一是使用
initWithRequest:<blah> delegate:<blah> startImmediately:FALSE
阻止您的连接立即启动然后安排您的NSURLConnection
另一个线程的运行循环上的实例使用:scheduleInRunLoop:forMode:- (注意:然后您必须通过调用 start 来启动连接 - 最好通过
NSOperation
+NSOperationQueue
来执行此操作。)
- (注意:然后您必须通过调用 start 来启动连接 - 最好通过
- 或者在
NSURLConnection
上使用这个静态方法创建/启动连接而不是执行分配/初始化:sendAsynchronousRequest:queue:completionHandler:
- (注意:此方法的效果与上述方法基本相同,但混淆了细节并夺走了您的一些控制权。)
说实话,我上面的快速回答不足以完成此类项目,您需要做一些研究来填补空白,特别是对于 NSOperationQueue,这就是 MVCNetworking 项目的所在会帮助你的。
网络连接是一个变化无常的野兽——即使它们在后台线程上运行,您也可以通过尝试同时执行太多工作来超时并终止连接!我会认真重新考虑一次打开数千个 NSURLConnections,并使用 NSOperationQueue
将有助于解决这个问题。
其他资源: 这是一个第 3 方库,可以让您的网络冒险不那么痛苦:
关于objective-c - 为什么 NSURLConnection 在发送大量请求时会超时?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9039760/