如何使用 AFNetworking 设置超时
-
25-10-2019 - |
题
我的项目正在使用 AFNetworking。
https://github.com/AFNetworking/AFNetworking
如何缩短超时时间?Atm 没有互联网连接,大约 2 分钟内不会触发故障阻止。太长了....
解决方案
更改超时间隔几乎可以肯定不是解决您所描述的问题的最佳解决方案。取而代之的是,似乎您真正想要的是HTTP客户端处理网络变得无法到达的情况,不是吗?
AFHTTPClient
已经有一种内置机制,可以让您知道何时丢失了Internet连接, -setReachabilityStatusChangeBlock:
.
请求在慢速网络上可能需要很长时间。最好相信iOS知道如何处理缓慢的连接,并说出这之间的区别,根本没有任何联系。
为了扩展我关于为什么应避免此线程中提到的其他方法的推理,这里有一些想法:
- 请求甚至可以在启动之前取消。出现一个请求,无法保证何时真正启动。
- 超时间隔不应取消长期运行的请求,尤其是发布。想象一下,如果您想下载或上传100MB视频。如果该请求在慢速3G网络上进行最佳,那么如果它花费的时间比预期的时间长,为什么要停止它?
- 正在做
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提到,看来Apple不允许更改此请求(在iOS 6及向上固定)。
正如@chrisopherpickslay指出的那样,这不是总体超时,这是接收(或发送数据)之间的超时。我不知道有任何明智地进行整体超时的方法。 Settimeoutinterval的Apple文档说:
超时间隔,几秒钟。如果在连接尝试期间,请求的闲置时间比超时间隔更长,则该请求被认为已计时。默认超时间隔为60秒。
您可以通过requestSerializer settimeoutInterval方法设置超时间隔。您可以从AfhttprequestoperationManager实例中获取请求serializer。
例如,以25秒的超时时间进行发布请求:
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
}];
我认为您目前必须手动修补它。
我正在为afhttpclient分类,并更改了
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
通过添加方法
[request setTimeoutInterval:10.0];
在 afhttpclient.m 第236行。当然,如果可以进行配置,那就很好,但是据我所知,目前这是不可能的。
终于发现了 如何使用异步邮政请求进行操作:
- (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];
}
我通过让我的服务器测试了此代码 sleep(aFewSeconds)
.
如果您需要执行同步邮政请求,请执行 不是 利用 [queue waitUntilAllOperationsAreFinished];
. 。而是使用与异步请求相同的方法,并等待您在选择器参数中传递的函数。
根据其他人的答案和 @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上工作。
我们不能用这样的计时器做到这一点:
在.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;
}
这里的“超时”定义有两种不同的含义。
超时如 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 报道。
我想展示如何实现这个 最后期限 在 AFNetworking 3.1 的 Swift(3 和 4)中。
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()
}
为了给出一个可测试的例子,这段代码应该打印“失败”而不是“成功”,因为在未来 0.0 秒立即超时:
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()
}
同意Matt,您不应该尝试更改TimeOutInterval。但是,您也不应依靠可及性检查来决定您要建立连接的天气,直到尝试之前,您才知道。
如Apple文档所述:
通常,您不应使用短时间间隔,而应为用户提供一种简单的方法来取消长期运行的操作。有关更多信息,请阅读“针对现实世界网络的设计”。