I created a simple HttpClient with AFNetworking.
HttpClient *client = [[HttpClient alloc] initWithTarget:(id)target
before:(SEL)before
success:(SEL)success
failure:(SEL)failure
timeout:(SEL)timeout]
So the controller can register callback function when it perfroms the HTTP request. And here is how I wrote the callback functions:
- (void)successMethod:(id)response {
// LogDebug(@"Success: %@", response);
self.lock = false;
if (self.target == nil || self.successCallback == nil) {
return;
}
if ([self.target respondsToSelector:self.successCallback]) {
[self.target performSelector:self.successCallback withObject:response];
}
}
But here I found a problem. When the request comes back fast, it works fine. But if the request comes back after a very longtime and meanwhile, user changes the view. Then it crashes the app and throw an exception like, selector can not be performed on a nil object.
So what I want know whether I did it in the right way and is there any way to solve this problem? What is the best practices to implement this?
=====
Update:
Sorry I didn't put the error log. But actually there is no much information I can get. Sometime there is even no log. I put a screenshot here and hope it can help.
I only get this and there is no exception log.
==== Update
And hope this can help
#import "HttpClient.h"
#import "AFNetworking.h"
#import "Logging.h"
@interface HttpClient ()
@property int retryCounter;
@property(nonatomic) NSString *action;
@property(nonatomic) NSString *url;
@property(nonatomic) NSDictionary *param;
@property(nonatomic) AFHTTPRequestOperationManager *manager;
@end
@implementation HttpClient
- (id)initWithTarget:(id)target
before:(SEL)before
success:(SEL)success
failure:(SEL)failure
timeout:(SEL)timeout {
self = [super init];
if (self) {
self.target = target;
self.before = before;
self.success = success;
self.failure = failure;
self.timeout = timeout;
self.lock = false;
self.retryMaxCounter = 2;
self.retryCounter = 0;
self.manager = [AFHTTPRequestOperationManager manager];
self.manager.requestSerializer = [AFJSONRequestSerializer serializer];
self.manager.responseSerializer = [AFJSONResponseSerializer serializer];
}
return self;
}
- (void)request:(NSString *)action
url:(NSString *)url
param:(NSDictionary *)param {
self.action = action;
self.url = url;
self.param = param;
[self beforeMethod];
[self request];
}
- (void)request {
if (self.lock) {
return;
}
if ([[self.action lowercaseString] isEqual:@"get"]) {
// Get
LogDebug(@"Send GET request.");
self.lock = true;
LogInfo(@"%@\n%@", self.url, self.param);
[self.manager GET:self.url
parameters:self.param
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self successMethod:responseObject];
}
failure:^(AFHTTPRequestOperation operation, NSError error) {
if ([operation.response statusCode] == 500) {
[self failureMethod:operation.responseObject];
} else {
[self timeoutMethod];
}
}];
} else if ([[self.action lowercaseString] isEqual:@"post"]) {
// POST
LogDebug(@"Send POST request.");
self.lock = true;
LogInfo(@"%@\n%@", self.url, self.param);
[self.manager POST:self.url
parameters:self.param
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self successMethod:responseObject];
}
failure:^(AFHTTPRequestOperation operation, NSError error) {
if ([operation.response statusCode] == 500) {
[self failureMethod:operation.responseObject];
} else {
[self timeoutMethod];
}
}];
} else {
LogError(@"Not supported request method.");
}
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- (void)beforeMethod {
LogDebug(@"Before requesting.");
if (self.target == nil || self.before == nil) {
return;
}
if ([self.target respondsToSelector:self.before]) {
[self.target performSelector:self.before];
}
}
- (void)successMethod:(id)success {
// LogDebug(@"Success: %@", success);
self.lock = false;
if (self.target == nil || self.success == nil) {
return;
}
if ([self.target respondsToSelector:self.success]) {
[self.target performSelector:self.success withObject:success];
}
}
- (void)failureMethod:(id)failure {
LogDebug(@"Failure: %@", failure);
self.lock = false;
if (self.target == nil || self.failure == nil) {
return;
}
if ([self.target respondsToSelector:self.failure]) {
[self.target performSelector:self.failure withObject:failure];
}
}
- (void)timeoutMethod {
LogError(@"Request timeout.");
self.lock = false;
self.retryCounter++;
if (self.retryCounter < self.retryMaxCounter) {
[self request];
} else {
if (self.target == nil || self.timeout == nil) {
return;
}
if ([self.target respondsToSelector:self.timeout]) {
[self.target performSelector:self.timeout];
}
}
}
#pragma clang diagnostic pop
@end
Thank you all very much!
==== Update
I found the problem. It was because I set the targe to assign
instead of weak
. For the difference between assign
and weak
, please refer to stackoverflow question