AFNetworking으로 시간 제한을 설정하는 방법


79

내 프로젝트는 AFNetworking을 사용하고 있습니다.

https://github.com/AFNetworking/AFNetworking

시간 제한을 어떻게 줄입니까? 인터넷에 연결되지 않은 ATM에서는 약 2 분 동안 실패 블록이 트리거되지 않습니다. 오래 기다려 ....


2
특히 performSelector:afterDelay:...기존 작업을 수동으로 취소하는 데 사용 하는 시간 제한 간격을 재정의하려는 모든 솔루션에 대해 강력히 권장 합니다. 자세한 내용은 내 대답을 참조하십시오.
mattt

답변:


110

제한 시간 간격을 변경하는 것은 설명하는 문제에 대한 최선의 해결책이 아닙니다. 대신, 실제로 원하는 것은 HTTP 클라이언트가 네트워크에 연결할 수 없게되는 것을 처리하는 것입니다.

AFHTTPClient이미 인터넷 연결이 끊어졌을 때 알려주는 내장 메커니즘이 있습니다 -setReachabilityStatusChangeBlock:.

느린 네트워크에서는 요청에 오랜 시간이 걸릴 수 있습니다. 느린 연결을 처리하는 방법을 알고 연결이 전혀없는 것과 그 차이를 알리려면 iOS를 신뢰하는 것이 좋습니다.


이 스레드에 언급 된 다른 접근 방식을 피해야하는 이유에 대한 내 추론을 확장하기 위해 다음과 같은 몇 가지 생각이 있습니다.

  • 요청은 시작되기 전에 취소 할 수 있습니다. 요청을 대기열에 넣는 것은 실제로 시작되는시기를 보장하지 않습니다.
  • 시간 초과 간격은 장기 실행 요청, 특히 POST를 취소해서는 안됩니다. 100MB 비디오를 다운로드하거나 업로드하려고한다고 상상해보십시오. 요청이 느린 3G 네트워크에서 가능한 한 최선을 다한다면 예상보다 조금 더 오래 걸리면 왜 불필요하게 중지 하시겠습니까?
  • 이렇게하면 performSelector:afterDelay:...멀티 스레드 애플리케이션에 위험 할 수 있습니다. 이것은 모호하고 디버그하기 어려운 경쟁 조건에 자신을 열어줍니다.

제목 임에도 불구하고 "인터넷 없음"사례에서 가능한 한 빨리 실패하는 방법에 대한 질문이기 때문이라고 생각합니다. 이를 수행하는 올바른 방법은 도달 가능성을 사용하는 것입니다. 타임 아웃을 사용하는 것은 작동하지 않거나 버그를 발견 / 수정하기 어려운 가능성이 높습니다.
Mihai Timar 2013-09-14

2
@mattt 귀하의 접근 방식에 동의하지만 시간 초과 기능이 정말로 필요한 연결 문제로 어려움을 겪고 있습니다. 문제는- "WiFi 핫스팟"을 노출하는 장치와 상호 작용하고 있습니다. 이 "WiFi 네트워크"에 연결되면 연결성 측면에서 장치에 대한 요청을 수행 할 수 있지만 연결되지 않았습니다. 반면에 장치 자체에 도달 할 수 있는지 여부를 확인하고 싶습니다. 이견있는 사람? 감사합니다
Stavash 2013

42
좋은 점하지만 제한 시간 설정하는 방법에 대한 질문에 대답하지 않습니다
최대 MacLeod는

3
AFNetworking Reference에서 "네트워크 연결성은 요청이 실패한 이유를 이해하는 데 사용할 수있는 진단 도구입니다. 요청 여부를 결정하는 데 사용해서는 안됩니다." 'setReachabilityStatusChangeBlock'섹션에서. 내가 'setReachabilityStatusChangeBlock'를 생각 그래서 ... 해결책 아니다
LKM

1
시간 제한을 수동으로 설정하는 것에 대해 두 번 생각해야하는 이유를 이해하지만이 대답은 질문에 대한 대답이 아닙니다. 내 앱은 인터넷 연결이 끊어지면 가능한 한 빨리 알아야합니다. AFNetworking을 사용하면 ReachabilityManager는 기기가 Wi-Fi 핫스팟에 연결된 경우를 감지하지 못하지만 핫스팟 자체에서 인터넷이 끊어집니다 (감지하는 데 몇 분 정도 소요됨). 따라서 시간 제한이 ~ 5 초인 Google에 요청하는 것이 해당 시나리오에서 최선의 선택 인 것 같습니다. 내가 놓친 게 없다면?
Tanner Semerad 2015

44

위의 mattt의 답변을 살펴볼 것을 강력히 권장합니다.이 답변은 그가 일반적으로 언급하는 문제에 해당하지 않지만 원래 포스터 질문의 경우 도달 가능성을 확인하는 것이 훨씬 더 적합합니다.

그러나 여전히 타임 아웃을 설정하고 싶다면 ( performSelector:afterDelay:등에 내재 된 모든 문제없이) 풀 리퀘스트 레고가 언급 한 주석 중 하나로이를 수행하는 방법을 설명합니다.

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 초입니다.


예, 시도해 보았지만 POST 요청을 할 때 작동하지 않습니다.
borisdiakur

7
사이드 노트 : Apple은 iOS 6에서이 문제를 해결했습니다
Raptor 2013 년

2
이것은 당신이 제안하는 것을 수행하지 않습니다. timeoutInterval요청 시간 제한이 아니라 유휴 타이머입니다. 따라서 위 코드가 타임 아웃 되려면 120 초 동안 데이터를 전혀 수신하지 않아야합니다. 데이터가 천천히 유입되면 요청이 무기한 계속 될 수 있습니다.
Christopher Pickslay 2013

그것이 어떻게 작동하는지에 대해 많이 말하지 않은 것은 공정한 요점입니다. 지금이 정보를 추가했으면 좋겠습니다. 감사합니다!
JosephH 2013


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
    }];

7

지금은 수동으로 패치해야한다고 생각합니다.

AFHTTPClient를 서브 클래 싱하고 있으며

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

추가하는 방법

[request setTimeoutInterval:10.0];

AFHTTPClient.m의 그 구성 할 수 있다면 물론 라인 (236)은 좋은 것입니다,하지만 지금까지 나는 그 순간에 불가능하다 볼 수있다.


7
고려해야 할 또 다른 사항은 Apple이 POST의 시간 제한을 재정의한다는 것입니다. 자동으로 4 분 정도의 시간이 소요되며 변경할 수 없습니다.
kcharwood

나는 또한 그것을 본다. 왜 그래야만하지? 연결이 실패하기 전에 사용자가 4 분 동안 기다리게하는 것은 좋지 않습니다.
slatvick

2
@KCHarwood 응답에 추가하려면. iOS 6부터 애플은 게시물의 시간 초과를 무시하지 않습니다. iOS 6에서 수정되었습니다.
ADAM

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 요청을 수행해야하는 경우를 사용 하지 마십시오[queue waitUntilAllOperationsAreFinished]; . 대신 비동기 요청과 동일한 접근 방식을 사용하고 selector 인수에서 전달하는 함수가 트리거 될 때까지 기다립니다.


18
아냐 아냐 아냐, 제발 실제 응용 프로그램이 사용하지 마십시오. 어떤 코드가 실제로하는 일은 간격 시간 후 요청 취소입니다 작업이 만들어집니다 시작, 하지 가 시작될거야 . 이로 인해 시작되기 전에 요청이 취소 될 수 있습니다.
mattt 2012

5
@mattt 작동하는 코드 샘플을 제공하십시오. 실제로 여러분이 설명하는 것은 정확히 제가 원하는 일입니다. 작업을 생성하는 순간 바로 시간 간격이 시작되기를 원합니다.
borisdiakur

레고 감사합니다! 저는 AFNetworking을 사용하고 있으며 AFNetworking의 작업 중 무작위로 약 10 %가 성공 또는 실패 블록을 호출하지 않습니다 [서버는 미국에 있고 테스트 사용자는 중국에 있습니다]. 사용자가 한 번에 너무 많은 요청을 보내는 것을 방지하기 위해 이러한 요청이 진행되는 동안 의도적으로 UI의 일부를 차단하기 때문에 이것은 저에게 큰 문제입니다. 결국 나는 지연이있는 performselector를 통해 완료 블록을 매개 변수로 전달하고! operation.isFinished 경우 블록이 실행되도록하는이 솔루션을 기반으로하는 버전을 구현했습니다.-Mattt : AFNetworking에 감사드립니다!
Corey

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에서 작동하도록 테스트되었습니다.


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

0

여기서 "시간 초과"정의에는 두 가지 다른 의미가 있습니다.

시간 초과 timeoutInterval

임의의 시간 간격보다 오랫동안 유휴 상태 (더 이상 전송 없음)가되면 요청을 삭제하려고합니다. 예 : timeoutInterval10 초로 설정 하고 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()
}

-2

Matt와 동의합니다. timeoutInterval을 변경해서는 안됩니다. 그러나 연결할 날씨를 결정하기 위해 도달 가능성 검사에 의존해서는 안됩니다. 시도 할 때까지 알 수 없습니다.

Apple 문서에 명시된대로 :

일반적으로 짧은 시간 제한 간격을 사용해서는 안되며 대신 사용자가 장기 실행 작업을 쉽게 취소 할 수있는 방법을 제공해야합니다. 자세한 내용은 "실제 네트워크 설계"를 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.