如何使用 AFNetworking 设置超时

我的项目是使用 AFNetworking。

Https://github.com/afnetworking/afnetworking

我怎样才能缩短暂停时间?没有互联网连接的自动取款机故障块大约2分钟内不会被触发。太久了。

69661 次浏览

我觉得你现在必须手动接入。

我子类化 AFHTTPClient 并更改了

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters

方法

[request setTimeoutInterval:10.0];

AFHTTPClient.m线236。 当然,如果可以配置的话会很好,但是据我所知,目前这是不可能的。

我强烈建议大家看看马特上面的答案——尽管这个答案与他提到的一般问题没有冲突,但对于原始的海报问题,检查可达性是一个更好的选择。

然而,如果你仍然想设置一个超时(没有 performSelector:afterDelay:等所有固有的问题,那么乐高提到的拉请求描述了一种方法来做到这一点作为一个评论,你只需要做:

NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];


AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];

但是注意@KCHARwood 提到的警告,苹果似乎不允许在 POST 请求中改变这一点(这在 iOS6及以上版本中是固定的)。

正如@ChrisopherPickslay 所指出的,这不是一个全面的超时,而是接收(或发送数据)之间的超时。我不知道有什么方法可以明智地进行全面超时。苹果的 setTimeoutInterval 文档说:

超时间隔,以秒为单位。如果在连接期间尝试 请求保持空闲的时间超过超时间隔,则请求 被认为已超时。默认的超时间隔为60 几秒钟。

最后,我们了解了 如何使用异步 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];
}

我通过让我的服务器 sleep(aFewSeconds)来测试这段代码。

如果需要执行同步 POST 请求,请使用 [queue waitUntilAllOperationsAreFinished];执行 没有。相反,使用与异步请求相同的方法,并等待在选择器参数中传递的函数被触发。

更改超时间隔几乎肯定不是您所描述的问题的最佳解决方案。相反,您似乎实际上希望 HTTP 客户机处理无法访问的网络,不是吗?

AFHTTPClient已经有一个内置的机制,让你知道什么时候互联网连接丢失,-setReachabilityStatusChangeBlock:

在慢速网络上,请求可能需要很长时间。最好是相信 iOS 知道如何处理缓慢的连接,并且能够区分它和根本没有连接之间的区别。


为了进一步说明为什么应该避免使用本帖中提到的其他方法,以下是我的一些想法:

  • 请求甚至可以在启动之前就被取消。对请求进行排队并不能保证它实际什么时候启动。
  • 超时间隔不应取消长时间运行的请求,特别是 POST。想象一下,如果您试图下载或上传一个100MB 的视频。如果请求在一个缓慢的3G 网络上尽可能地进行,如果花费的时间比预期的要长,为什么要不必要地停止请求呢?
  • 在多线程应用程序中执行 performSelector:afterDelay:...可能是危险的,这将使自己面临模糊且难以调试的竞态条件。

根据其他人的回答和@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

测试在 iOS6上工作。

我们不能用这样的计时器吗:

在.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;
}

可以通过 requestSerializer setTimeoutInterval 方法设置超时间隔。

例如,执行超时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
}];

同意 Matt 的观点,你不应该试图改变时间间隔。但是你也不应该依赖可达性检查来决定你是否要建立连接,你不尝试是不会知道的。

正如苹果文件所述:

一般来说,不应该使用短的超时间隔,而应该为用户提供一种取消长时间运行的操作的简单方法。更多信息,请阅读“为现实世界网络设计”。

这里的“超时”定义有两种不同的含义。

timeoutInterval的超时

当请求空闲(不再传输)超过任意时间间隔时,您希望删除该请求。例如: 您将 timeoutInterval设置为10秒,您在12:00:00开始请求,它可能传输一些数据到12:00:23,然后连接将在12:00:33超时。这个案件几乎涵盖了所有的答案(包括约瑟夫,穆斯塔法阿卜杜拉蒂夫,科尼利厄斯和古尔帕塔普辛格)。

timeoutDeadline的超时

您希望在到达任意发生的最后期限时删除请求。例如: 你将 deadline设置为10秒,你在12:00:00开始你的请求,它可能会尝试传输一些数据到12:00:23,但是连接会在12:00:10超时。这个案子由 Borisdiakur 负责。

我想展示如何在 Swift (3和4)中为 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()
}

为了给出一个可测试的例子,这段代码应该打印“失败”而不是“成功”,因为在未来的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()
}