ios 如何使用 AFNetworking 设置超时
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8304560/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to set a timeout with AFNetworking
提问by jennas
My project is using AFNetworking.
我的项目正在使用 AFNetworking。
https://github.com/AFNetworking/AFNetworking
https://github.com/AFNetworking/AFNetworking
How do I dial down the timeout? Atm with no internet connection the fail block isn't triggered for what feels like about 2 mins. Waay to long....
我如何拨出超时?没有互联网连接的自动取款机在大约 2 分钟内不会触发失败块。好久不见....
回答by mattt
Changing the timeout interval is almost certainly not the best solution to the problem you're describing. Instead, it seems like what you actually want is for the HTTP client to handle the network becoming unreachable, no?
更改超时间隔几乎肯定不是您所描述问题的最佳解决方案。相反,看起来您真正想要的是让 HTTP 客户端处理网络变得无法访问,不是吗?
AFHTTPClient
already has a built-in mechanism to let you know when internet connection is lost, -setReachabilityStatusChangeBlock:
.
AFHTTPClient
已经有一个内置机制可以让您知道互联网连接何时丢失,-setReachabilityStatusChangeBlock:
.
Requests can take a long time on slow networks. It's better to trust iOS to know how to deal slow connections, and tell the difference between that and having no connection at all.
在慢速网络上请求可能需要很长时间。最好相信 iOS 知道如何处理慢速连接,并告诉它和根本没有连接之间的区别。
To expand on my reasoning as to why other approaches mentioned in this thread should be avoided, here are a few thoughts:
为了扩展我关于为什么应该避免本线程中提到的其他方法的推理,这里有一些想法:
- Requests can be cancelled before they're even started. Enqueueing a request makes no guarantees about when it actually starts.
- Timeout intervals shouldn't cancel long-running requests—especially POST. Imagine if you were trying to download or upload a 100MB video. If the request is going along as best it can on a slow 3G network, why would you needlessly stop it if it's taking a bit longer than expected?
- Doing
performSelector:afterDelay:...
can be dangerous in multi-threaded applications. This opens oneself up to obscure and difficult-to-debug race conditions.
- 甚至可以在开始之前取消请求。将请求排入队列并不能保证它实际启动的时间。
- 超时间隔不应取消长时间运行的请求——尤其是 POST。想象一下,如果您尝试下载或上传 100MB 的视频。如果请求在缓慢的 3G 网络上尽可能地进行,如果它花费的时间比预期的要长一点,为什么你会不必要地停止它?
performSelector:afterDelay:...
在多线程应用程序中这样做可能很危险。这让自己面临模糊且难以调试的竞争条件。
回答by JosephH
I strongly recommend looking at mattt's answer above - although this answer doesn't fall foul of the problems he mentions in general, for the original posters question, checking reachability is a much better fit.
我强烈建议查看上面马特的回答——尽管这个回答与他一般提到的问题没有冲突,但对于原始海报问题,检查可达性更合适。
However, if you do still want to set a timeout (without all the problems inherent in performSelector:afterDelay:
etc, then the pull request Lego mentions describes a way to do this as one of the comments, you just do:
但是,如果您仍然想设置超时(没有performSelector:afterDelay:
等中固有的所有问题,那么乐高提到的拉取请求描述了一种方法作为评论之一来执行此操作,您只需执行以下操作:
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
but see the caveat @KCHarwood mentions that it appears Apple don't allow this to be changed for POST requests (which is fixed in iOS 6 and upwards).
但是看到警告@KCHarwood 提到,Apple 似乎不允许为 POST 请求更改此设置(在 iOS 6 及更高版本中已修复)。
As @ChrisopherPickslay points out, this isn't an overall timeout, it's a timeout between receiving (or sending data). I'm not aware of any way to sensibly do an overall timeout. The Apple documentation for setTimeoutInterval says:
正如@ChrisopherPickslay 指出的那样,这不是整体超时,而是接收(或发送数据)之间的超时。我不知道有什么方法可以明智地进行整体超时。setTimeoutInterval 的 Apple 文档说:
The timeout interval, in seconds. If during a connection attempt the request remains idle for longer than the timeout interval, the request is considered to have timed out. The default timeout interval is 60 seconds.
超时间隔,以秒为单位。如果在连接尝试期间请求保持空闲的时间超过超时间隔,则认为该请求已超时。默认超时间隔为 60 秒。
回答by Mostafa Abdellateef
You can set the timeout interval through requestSerializer setTimeoutInterval method.You can get the requestSerializer from an AFHTTPRequestOperationManager instance.
您可以通过 requestSerializer setTimeoutInterval 方法设置超时间隔。您可以从 AFHTTPRequestOperationManager 实例中获取 requestSerializer。
For example to do a post request with a timeout of 25 second :
例如做一个超时为 25 秒的 post 请求:
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
}];
回答by borisdiakur
Finally found outhow to do it with an asynchronous POST request:
终于找到了如何使用异步 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];
}
I tested this code by letting my server sleep(aFewSeconds)
.
我通过让我的服务器sleep(aFewSeconds)
.
If you need to do a synchronous POST request, do NOTuse [queue waitUntilAllOperationsAreFinished];
. Instead use the same approach as for the asynchronous request and wait for the function to be triggered which you pass on in the selector argument.
如果您需要执行同步 POST 请求,请不要使用[queue waitUntilAllOperationsAreFinished];
. 而是使用与异步请求相同的方法,并等待在选择器参数中传递的函数被触发。
回答by Cornelius
I think you have to patch that in manually at the moment.
我认为你现在必须手动修补它。
I am subclassing AFHTTPClient and changed the
我正在继承 AFHTTPClient 并更改了
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
method by adding
通过添加方法
[request setTimeoutInterval:10.0];
in AFHTTPClient.mline 236. Of course it would be good if that could be configured, but as far as I see that is not possible at the moment.
在AFHTTPClient.m第 236 行中。当然,如果可以配置它会很好,但据我所知,目前这是不可能的。
回答by Gurpartap Singh
Based on others' answers and @mattt's suggestion on related project issues, here is a drop-in quickie if you are subclassing AFHTTPClient
:
根据其他人的回答和@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
Tested to work on iOS 6.
经测试可在 iOS 6 上运行。
回答by Ula? Sancak
Can't we do this with a timer like this:
我们不能用这样的计时器来做到这一点:
In .h file
在 .h 文件中
{
NSInteger time;
AFJSONRequestOperation *operation;
}
In .m file
在 .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;
}
回答by C?ur
There are two different meanings on the "timeout" definition here.
这里的“超时”定义有两种不同的含义。
Timeout as in timeoutInterval
超时如 timeoutInterval
You want to drop a request when it becomes idle (no more transfer) for longer than an arbitrary interval of time. Example: you set timeoutInterval
to 10 seconds, you start your request at 12:00:00, it may transfer some data until 12:00:23, then connection will timeout at 12:00:33. This case is covered by almost all answers here (including JosephH, Mostafa Abdellateef, Cornelius and Gurpartap Singh).
您希望在空闲(不再传输)超过任意时间间隔时删除请求。例如:你设置timeoutInterval
为 10 秒,你在 12:00:00 开始你的请求,它可能会传输一些数据到 12:00:23,然后连接将在 12:00:33 超时。这个案例几乎涵盖了这里的所有答案(包括 JosephH、Mostafa Abdellateef、Cornelius 和 Gurpartap Singh)。
Timeout as in timeoutDeadline
超时如 timeoutDeadline
You want to drop a request when it reaches a deadline happening arbitrary later. Example: you set deadline
to 10 seconds in the future, you start your request at 12:00:00, it may attempt to transfer some data until 12:00:23, but connection will timeout earlier at 12:00:10. This case is covered by borisdiakur.
您希望在到达任意稍后发生的截止日期时删除请求。示例:您设置deadline
为 10 秒后,您在 12:00:00 开始请求,它可能会尝试传输一些数据,直到 12:00:23,但连接将在 12:00:10 提前超时。这个案例由 borisdiakur 报道。
I'd like to show how to implement this deadlinein Swift (3 and 4) for AFNetworking 3.1.
我想展示如何在 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()
}
And to give a testable example, this code should print "failure" instead of "success" because of the immediate timeout at 0.0 seconds in the future:
举一个可测试的例子,这段代码应该打印“失败”而不是“成功”,因为在未来 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()
}
回答by woof
Agree with Matt, you shouldn't try change the timeoutInterval. But you also should not rely on reachability check to decide weather you are gonna make the connection, you don't know until you try.
同意马特,您不应该尝试更改 timeoutInterval。但是你也不应该依赖可达性检查来决定你要建立连接的天气,直到你尝试才知道。
As stated by Apple document:
正如 Apple 文档所述:
As a general rule, you should not use short timeout intervals, and instead, should provide an easy way for the user to cancel a long-running operation. For more information, read “Designing for Real-World Networks”.
作为一般规则,您不应使用较短的超时间隔,而应为用户提供一种简单的方法来取消长时间运行的操作。有关更多信息,请阅读“为真实世界的网络设计”。