내 프로젝트는 AFNetworking을 사용하고 있습니다.
https://github.com/AFNetworking/AFNetworking
시간 제한을 어떻게 줄입니까? 인터넷에 연결되지 않은 ATM에서는 약 2 분 동안 실패 블록이 트리거되지 않습니다. 오래 기다려 ....
내 프로젝트는 AFNetworking을 사용하고 있습니다.
https://github.com/AFNetworking/AFNetworking
시간 제한을 어떻게 줄입니까? 인터넷에 연결되지 않은 ATM에서는 약 2 분 동안 실패 블록이 트리거되지 않습니다. 오래 기다려 ....
답변:
제한 시간 간격을 변경하는 것은 설명하는 문제에 대한 최선의 해결책이 아닙니다. 대신, 실제로 원하는 것은 HTTP 클라이언트가 네트워크에 연결할 수 없게되는 것을 처리하는 것입니다.
AFHTTPClient
이미 인터넷 연결이 끊어졌을 때 알려주는 내장 메커니즘이 있습니다 -setReachabilityStatusChangeBlock:
.
느린 네트워크에서는 요청에 오랜 시간이 걸릴 수 있습니다. 느린 연결을 처리하는 방법을 알고 연결이 전혀없는 것과 그 차이를 알리려면 iOS를 신뢰하는 것이 좋습니다.
이 스레드에 언급 된 다른 접근 방식을 피해야하는 이유에 대한 내 추론을 확장하기 위해 다음과 같은 몇 가지 생각이 있습니다.
performSelector:afterDelay:...
멀티 스레드 애플리케이션에 위험 할 수 있습니다. 이것은 모호하고 디버그하기 어려운 경쟁 조건에 자신을 열어줍니다.위의 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 초입니다.
timeoutInterval
요청 시간 제한이 아니라 유휴 타이머입니다. 따라서 위 코드가 타임 아웃 되려면 120 초 동안 데이터를 전혀 수신하지 않아야합니다. 데이터가 천천히 유입되면 요청이 무기한 계속 될 수 있습니다.
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
}];
지금은 수동으로 패치해야한다고 생각합니다.
AFHTTPClient를 서브 클래 싱하고 있으며
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
추가하는 방법
[request setTimeoutInterval:10.0];
에 AFHTTPClient.m의 그 구성 할 수 있다면 물론 라인 (236)은 좋은 것입니다,하지만 지금까지 나는 그 순간에 불가능하다 볼 수있다.
마지막으로 비동기 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 인수에서 전달하는 함수가 트리거 될 때까지 기다립니다.
관련 프로젝트 문제에 대한 다른 사람의 답변과 @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에서 작동하도록 테스트되었습니다.
다음과 같은 타이머로이 작업을 수행 할 수 없습니다.
.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;
}
여기서 "시간 초과"정의에는 두 가지 다른 의미가 있습니다.
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에 의해 보호됩니다.
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()
}
performSelector:afterDelay:...
기존 작업을 수동으로 취소하는 데 사용 하는 시간 제한 간격을 재정의하려는 모든 솔루션에 대해 강력히 권장 합니다. 자세한 내용은 내 대답을 참조하십시오.