我的项目正在使用AFNetworking。
https://github.com/AFNetworking/AFNetworking
我该如何降低超时时间?目前在没有网络连接时,失败块没有被触发的时间感觉大约是2分钟,太长了...
我的项目正在使用AFNetworking。
https://github.com/AFNetworking/AFNetworking
我该如何降低超时时间?目前在没有网络连接时,失败块没有被触发的时间感觉大约是2分钟,太长了...
改变超时时间几乎肯定不是解决您描述的问题的最佳方法。相反,似乎你真正想要的是HTTP客户端处理网络连接不可达的情况,对吗?
AFHTTPClient
已经内置了一个机制,让你知道何时失去互联网连接,-setReachabilityStatusChangeBlock:
。
在缓慢的网络上请求可能需要很长时间。最好信任 iOS 知道如何处理缓慢的连接,并区分这种情况和根本没有连接的情况。
以下是我认为为什么本文提到的其他方法应该避免的原因:
performSelector:afterDelay:...
可能会存在危险。这会使自己暴露于晦涩难懂且难以调试的竞态条件。我强烈建议查看mattt在上面的回答-尽管这个答案通常不会遇到他提到的问题,但对于原始帖子的问题,检查可达性更加合适。
然而,如果你仍然想设置超时(不涉及所有与performSelector:afterDelay:
等相关的问题),那么Lego提到的拉请求描述了一种方法来执行此操作,你只需执行以下操作:
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
但是请看@KCHarwood提到的警告,似乎苹果不允许更改POST请求的超时时间(这在iOS 6及以上版本中已经修复)。
正如@ChrisopherPickslay指出的那样,这不是一个总超时时间,而是接收或发送数据之间的超时时间。我不知道有什么合理的方法来实现总体超时。Apple的setTimeoutInterval文档说:
超时时间,以秒为单位。如果在连接尝试期间请求保持空闲状态的时间超过超时时间,则请求被视为已超时。默认的超时时间为60秒。
timeoutInterval
是一个空闲计时器,而不是请求超时。因此,如果在 120 秒内没有接收到任何数据,上述代码才会超时。如果数据缓慢地流入,请求可能会无限期地继续。 - Christopher Pickslay NSDictionary *params = @{@"par1": @"value1",
@"par2": @"value2"};
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setTimeoutInterval:25]; //Time out after 25 seconds
[manager POST:@"URL" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Success call back bock
NSLog(@"Request completed with response: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Failure callback block. This block may be called due to time out or any other failure reason
}];
终于找到了如何通过异步POST请求完成它:
- (void)timeout:(NSDictionary*)dict {
NDLog(@"timeout");
AFHTTPRequestOperation *operation = [dict objectForKey:@"operation"];
if (operation) {
[operation cancel];
}
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
[self perform:[[dict objectForKey:@"selector"] pointerValue] on:[dict objectForKey:@"object"] with:nil];
}
- (void)perform:(SEL)selector on:(id)target with:(id)object {
if (target && [target respondsToSelector:selector]) {
[target performSelector:selector withObject:object];
}
}
- (void)doStuffAndNotifyObject:(id)object withSelector:(SEL)selector {
// AFHTTPRequestOperation asynchronous with selector
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
@"doStuff", @"task",
nil];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseURL]];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"POST" path:requestURL parameters:params];
[httpClient release];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
operation, @"operation",
object, @"object",
[NSValue valueWithPointer:selector], @"selector",
nil];
[self performSelector:@selector(timeout:) withObject:dict afterDelay:timeout];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timeout:) object:dict];
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
[self perform:selector on:object with:[operation responseString]];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NDLog(@"fail! \nerror: %@", [error localizedDescription]);
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timeout:) object:dict];
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
[self perform:selector on:object with:nil];
}];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[[AFNetworkActivityIndicatorManager sharedManager] incrementActivityCount];
[queue addOperation:operation];
}
睡眠(几秒钟)
来测试了这段代码。[queue waitUntilAllOperationsAreFinished];
。而是使用与异步请求相同的方法,并等待在选择器参数中传递的函数被触发。我认为目前你需要手动打补丁。
我正在子类化AFHTTPClient并更改了
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
通过添加方法
[request setTimeoutInterval:10.0];
在AFHTTPClient.m的第236行。
当然,如果可以配置那就太好了,但据我所见目前不可能实现。根据其他人的答案和相关项目问题上@mattt的建议,如果您正在子类化AFHTTPClient
,这里有一个快速可用的解决方法:
@implementation SomeAPIClient // subclass of AFHTTPClient
// ...
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
NSMutableURLRequest *request = [super requestWithMethod:method path:path parameters:parameters];
[request setTimeoutInterval:120];
return request;
}
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block {
NSMutableURLRequest *request = [super requestWithMethod:method path:path parameters:parameters];
[request setTimeoutInterval:120];
return request;
}
@end
已经测试通过iOS 6。
这里有两个不同的“超时”定义意义。
timeoutInterval
的超时当请求在任意时间间隔内变得空闲(没有更多传输)时,您希望放弃该请求。例如:您将timeoutInterval
设置为10秒,您在12:00:00开始请求,它可能会传输一些数据直到12:00:23,然后连接将在12:00:33超时。几乎所有答案都涵盖了这种情况(包括JosephH、Mostafa Abdellateef、Cornelius和Gurpartap Singh)。
timeoutDeadline
的超时当请求达到任意后期截止日期时,您希望放弃该请求。例如:您将deadline
设置为未来的10秒,您在12:00:00开始请求,它可能会尝试传输一些数据直到12:00:23,但连接将在12:00:10之前超时。borisdiakur涵盖了这种情况。
我想展示如何在Swift(3和4)中实现此deadline,适用于AFNetworking 3.1。
let sessionManager = AFHTTPSessionManager(baseURL: baseURL)
let request = sessionManager.post(endPoint, parameters: parameters, progress: { ... }, success: { ... }, failure: { ... })
// timeout deadline at 10 seconds in the future
DispatchQueue.global().asyncAfter(deadline: .now() + 10.0) {
request?.cancel()
}
let sessionManager = AFHTTPSessionManager(baseURL: URL(string: "https://example.com"))
sessionManager.responseSerializer = AFHTTPResponseSerializer()
let request = sessionManager.get("/", parameters: nil, progress: nil, success: { _ in
print("success")
}, failure: { _ in
print("failure")
})
// timeout deadline at 0 seconds in the future
DispatchQueue.global().asyncAfter(deadline: .now() + 0.0) {
request?.cancel()
}
我们不能用像这样的计时器来做吗:
在 .h 文件中
{
NSInteger time;
AFJSONRequestOperation *operation;
}
在 .m 文件中
-(void)AFNetworkingmethod{
time = 0;
NSTtimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(startTimer:) userInfo:nil repeats:YES];
[timer fire];
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
[self operationDidFinishLoading:JSON];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
[self operationDidFailWithError:error];
}];
[operation setJSONReadingOptions:NSJSONReadingMutableContainers];
[operation start];
}
-(void)startTimer:(NSTimer *)someTimer{
if (time == 15&&![operation isFinished]) {
time = 0;
[operation invalidate];
[operation cancel];
NSLog(@"Timeout");
return;
}
++time;
}
同意Matt的观点,你不应该尝试更改timeoutInterval。但是你也不应该依赖可达性检查来决定是否要建立连接,直到你尝试过才知道。
正如苹果文档所述:
通常情况下,你不应该使用短的超时时间间隔,而应该提供一种简单的方法让用户取消长时间运行的操作。有关更多信息,请阅读“为真实世界的网络设计”。
performSelector:afterDelay:...
手动取消现有操作的解决方案。请查看我的答案以获取更多详细信息。 - mattt