Dự án của tôi đang sử dụng AFNetworking.
https://github.com/AFNetworking/AFNetworking
Làm cách nào để quay số thời gian chờ? Atm không có kết nối internet, khối lỗi không được kích hoạt trong khoảng 2 phút. Waay to long ....
Dự án của tôi đang sử dụng AFNetworking.
https://github.com/AFNetworking/AFNetworking
Làm cách nào để quay số thời gian chờ? Atm không có kết nối internet, khối lỗi không được kích hoạt trong khoảng 2 phút. Waay to long ....
Câu trả lời:
Thay đổi khoảng thời gian chờ gần như chắc chắn không phải là giải pháp tốt nhất cho vấn đề bạn đang mô tả. Thay vào đó, có vẻ như những gì bạn thực sự muốn là máy khách HTTP xử lý mạng trở nên không thể truy cập được, phải không?
AFHTTPClient
đã có một cơ chế tích hợp để cho bạn biết khi mất kết nối internet , -setReachabilityStatusChangeBlock:
.
Yêu cầu có thể mất nhiều thời gian trên mạng chậm. Tốt hơn hết bạn nên tin tưởng iOS để biết cách xử lý kết nối chậm và phân biệt giữa điều đó và không có kết nối.
Để mở rộng lý luận của tôi về lý do tại sao nên tránh các cách tiếp cận khác được đề cập trong chủ đề này, đây là một vài suy nghĩ:
performSelector:afterDelay:...
có thể nguy hiểm trong các ứng dụng đa luồng. Điều này mở ra cho bản thân những điều kiện cuộc đua khó hiểu và khó gỡ lỗi.Tôi thực sự khuyên bạn nên xem câu trả lời của mattt ở trên - mặc dù câu trả lời này không liên quan đến các vấn đề mà anh ấy đề cập nói chung, đối với câu hỏi áp phích ban đầu, kiểm tra khả năng tiếp cận là phù hợp hơn nhiều.
Tuy nhiên, nếu bạn vẫn muốn đặt thời gian chờ (mà không có tất cả các vấn đề vốn có trong performSelector:afterDelay:
v.v., thì yêu cầu kéo mà Lego đề cập mô tả một cách để thực hiện điều này như một trong các nhận xét, bạn chỉ cần làm:
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
nhưng hãy xem cảnh báo @KCHarwood đề cập rằng có vẻ như Apple không cho phép thay đổi điều này đối với các yêu cầu POST (đã được khắc phục trong iOS 6 trở lên).
Như @ChrisopherPickslay đã chỉ ra, đây không phải là thời gian chờ tổng thể mà là thời gian chờ giữa việc nhận (hoặc gửi dữ liệu). Tôi không biết bất kỳ cách nào để thực hiện thời gian chờ tổng thể một cách hợp lý. Tài liệu của Apple cho setTimeoutInterval cho biết:
Khoảng thời gian chờ, tính bằng giây. Nếu trong khi cố gắng kết nối, yêu cầu vẫn ở chế độ chờ lâu hơn khoảng thời gian chờ, yêu cầu được coi là đã hết thời gian chờ. Khoảng thời gian chờ mặc định là 60 giây.
timeoutInterval
là bộ hẹn giờ nhàn rỗi, không phải thời gian chờ yêu cầu. Vì vậy, bạn sẽ không nhận được dữ liệu nào trong 120 giây để mã trên hết thời gian chờ. Nếu dữ liệu từ từ chảy vào, yêu cầu có thể tiếp tục vô thời hạn.
Bạn có thể đặt khoảng thời gian chờ thông qua phương thức requestSerializer setTimeoutInterval.Bạn có thể lấy requestSerializer từ phiên bản AFHTTPRequestOperationManager.
Ví dụ để thực hiện một yêu cầu đăng bài với thời gian chờ là 25 giây:
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
}];
Tôi nghĩ bạn phải vá lỗi đó bằng tay vào lúc này.
Tôi đang phân loại AFHTTPClient và đã thay đổi
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
phương pháp bằng cách thêm
[request setTimeoutInterval:10.0];
trong AFHTTPClient.m dòng 236. Tất nhiên sẽ rất tốt nếu điều đó có thể được định cấu hình, nhưng theo tôi thấy điều đó là không thể vào lúc này.
Cuối cùng đã tìm ra cách thực hiện với yêu cầu POST không đồng bộ:
- (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];
}
Tôi đã thử nghiệm mã này bằng cách cho phép máy chủ của tôi sleep(aFewSeconds)
.
Nếu bạn cần thực hiện yêu cầu ĐĂNG đồng bộ, KHÔNG sử dụng [queue waitUntilAllOperationsAreFinished];
. Thay vào đó, hãy sử dụng phương pháp tương tự như đối với yêu cầu không đồng bộ và đợi hàm được kích hoạt mà bạn chuyển vào đối số bộ chọn.
Dựa trên câu trả lời của những người khác và đề xuất của @ mattt về các vấn đề liên quan của dự án, đây là một tài liệu đăng ký nhanh nếu bạn đang xếp lớp con 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
Đã thử nghiệm hoạt động trên iOS 6.
Chúng ta không thể làm điều này với một bộ đếm thời gian như thế này:
Trong tệp .h
{
NSInteger time;
AFJSONRequestOperation *operation;
}
Trong tệp .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;
}
Có hai ý nghĩa khác nhau về định nghĩa "thời gian chờ" ở đây.
timeoutInterval
Bạn muốn hủy một yêu cầu khi nó không hoạt động (không chuyển nữa) lâu hơn một khoảng thời gian tùy ý. Ví dụ: bạn đặt timeoutInterval
thành 10 giây, bạn bắt đầu yêu cầu của mình lúc 12:00:00, nó có thể chuyển một số dữ liệu cho đến 12:00:23, sau đó kết nối sẽ hết lúc 12:00:33. Trường hợp này được đề cập bởi hầu hết các câu trả lời ở đây (bao gồm JosephH, Mostafa Abdionaryef, Cornelius và Gurpartap Singh).
timeoutDeadline
Bạn muốn bỏ một yêu cầu khi nó đến thời hạn tùy ý xảy ra sau đó. Ví dụ: bạn đặt deadline
thành 10 giây trong tương lai, bạn bắt đầu yêu cầu của mình lúc 12:00:00, nó có thể cố gắng chuyển một số dữ liệu cho đến 12:00:23, nhưng kết nối sẽ hết thời gian sớm hơn lúc 12:00:10. Trường hợp này được bảo hiểm bởi borisdiakur.
Tôi muốn chỉ ra cách thực hiện thời hạn này trong Swift (3 và 4) cho 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()
}
Và để đưa ra một ví dụ có thể kiểm tra, mã này sẽ in "thất bại" thay vì "thành công" vì hết thời gian chờ ngay lập tức là 0,0 giây trong tương lai:
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()
}
Đồng ý với Matt, bạn không nên thử thay đổi timeoutInterval. Nhưng bạn cũng không nên dựa vào kiểm tra khả năng kết nối để quyết định thời tiết bạn sẽ thực hiện kết nối, bạn không biết cho đến khi bạn thử.
Như tài liệu của Apple đã nêu:
Theo nguyên tắc chung, bạn không nên sử dụng các khoảng thời gian chờ ngắn và thay vào đó, nên cung cấp một cách dễ dàng để người dùng hủy một hoạt động đang chạy trong thời gian dài. Để biết thêm thông tin, hãy đọc “Thiết kế cho các mạng trong thế giới thực”.
performSelector:afterDelay:...
để hủy thủ công các hoạt động hiện có. Vui lòng xem câu trả lời của tôi để biết thêm chi tiết.