如何使用AFNetworking设置超时

jen*_*nas 79 networking xcode objective-c ios afnetworking

我的项目正在使用AFNetworking.

https://github.com/AFNetworking/AFNetworking

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

mat*_*ttt 109

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

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

请求在慢速网络上需要很长时间.最好是相信iOS知道如何处理慢速连接,并告诉它之间的区别并且完全没有连接.


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

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

  • 好点,但没有回答关于如何设置超时的问题 (40认同)
  • 在AFNetworking Reference,"网络可达性是一种诊断工具,可用于了解请求可能失败的原因.它不应用于确定是否发出请求." 在'setReachabilityStatusChangeBlock'部分.所以我认为'setReachabilityStatusChangeBlock'不是解决方案...... (3认同)
  • @mattt虽然我同意你的方法,但我正在努力解决我真正需要超时功能的连接问题.问题是 - 我正在与一个暴露"WiFi热点"的设备进行交互.当连接到这个"WiFi网络"时,就可达性而言,虽然我能够对设备执行请求,但我没有连接.另一方面,我确实希望能够确定设备本身是否可访问.有什么想法吗?谢谢 (2认同)

Jos*_*phH 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];
Run Code Online (Sandbox Code Playgroud)

但请注意警告@KCHarwood提到Apple似乎不允许更改POST请求(在iOS 6及更高版本中修复).

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

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

  • *旁注:*Apple在iOS 6中修复此问题 (7认同)
  • 这不符合你的建议.`timeoutInterval`是空闲计时器,而不是请求超时.因此,您必须在120秒内完全没有收到任何数据才能使上述代码超时.如果数据慢慢流入,请求可以无限期地继续. (2认同)

Mos*_*eef 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
    }];
Run Code Online (Sandbox Code Playgroud)


Cor*_*ius 7

我想你现在必须手动修补它.

我是AFHTTPClient的子类并改变了

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
Run Code Online (Sandbox Code Playgroud)

方法加入

[request setTimeoutInterval:10.0];
Run Code Online (Sandbox Code Playgroud)

AFHTTPClient.m第236行.当然,如果可以配置它会很好,但据我所知,目前是不可能的.

  • 另一件需要考虑的事情是,Apple会覆盖POST的超时.我想自动4分钟,你不能改变它. (7认同)
  • 添加到@KCHarwood的回复.从iOS 6开始苹果不会覆盖帖子的超时.这已在iOS 6中修复. (2认同)

bor*_*kur 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];
}
Run Code Online (Sandbox Code Playgroud)

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

如果您需要执行同步POST请求,请不要使用[queue waitUntilAllOperationsAreFinished];.而是使用与异步请求相同的方法,并等待在selector参数中传递的函数被触发.

  • 不,不,不,不,_please_不要在实际应用中使用它.您的代码实际执行的操作是在创建操作后开始的时间间隔_之后取消请求,__ not__何时启动_.这可能导致请求在开始之前被取消. (18认同)
  • @mattt请提供有效的代码示例.实际上你所描述的正是我想要发生的事情:我希望时间间隔在我创建操作的那一刻开始滴答作响. (5认同)

Gur*_*ngh 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
Run Code Online (Sandbox Code Playgroud)

经测试可在iOS 6上运行.