如何使用AFNetworking设置超时

时间:2011-11-29 01:06:46

标签: objective-c ios xcode networking afnetworking

我的项目正在使用AFNetworking。

https://github.com/AFNetworking/AFNetworking

如何拨打超时?在没有互联网连接的情况下,故障块不会触发约2分钟的感觉。等不及多久......

9 个答案:

答案 0 :(得分:109)

更改超时间隔几乎肯定不是您所描述问题的最佳解决方案。相反,看起来你真正想要的是HTTP客户端处理网络无法访问,不是吗?

AFHTTPClient已有内置机制,可让您知道何时丢失互联网连接-setReachabilityStatusChangeBlock:

请求在慢速网络上需要很长时间。相信iOS可以更好地了解如何处理慢速连接,并告诉它之间的区别,并且完全没有连接。


为了扩展我的理由,为什么应该避免在这个线程中提到的其他方法,这里有一些想法:

  • 请求可以在它们开始之前取消。排队请求并不能保证它何时实际启动。
  • 超时间隔不应取消长时间运行的请求 - 尤其是POST。想象一下,如果您尝试下载或上传100MB视频。如果请求在慢速3G网络上尽可能地发挥作用,那么如果它比预期花费的时间更长,为什么你会不必要地停止它呢?
  • 在多线程应用程序中执行performSelector:afterDelay:...会很危险。这使自己面临着模糊和难以调试的竞争条件。

答案 1 :(得分:43)

我强烈建议你查看上面的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似乎不允许更改POST请求(在iOS 6及更高版本中已修复)。

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

  

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

答案 2 :(得分:26)

您可以通过requestSerializer setTimeoutInterval方法设置超时间隔。您可以从AFHTTPRequestOperationManager实例获取requestSerializer。

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

答案 3 :(得分:7)

我认为你现在必须手动修补它。

我正在对AFHTTPClient进行子类化并更改了

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

添加方法

[request setTimeoutInterval:10.0];
AFHTTPClient.m 第236行

。 当然,如果可以配置它会很好,但据我所知目前是不可能的。

答案 4 :(得分:7)

终于找到了如何使用异步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请求,请执行 NOT 使用[queue waitUntilAllOperationsAreFinished];。而是使用与异步请求相同的方法,并等待在selector参数中传递的函数被触发。

答案 5 :(得分:5)

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

答案 6 :(得分:0)

我们不能用这样的计时器来做这件事:

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

答案 7 :(得分:0)

&#34;超时&#34;有两种不同的含义。在这里定义。

超时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)中为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()
}

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

答案 8 :(得分:-2)

同意Matt,你不应该尝试更改timeoutInterval。但你也不应该依靠可达性检查来决定你要建立连接的天气,直到你尝试才知道。

如Apple文件所述:

  

作为一般规则,您不应使用短暂超时间隔,而应为用户提供取消长时间运行操作的简便方法。有关更多信息,请阅读“为真实网络设计”。