2015-04-29 5 views
-2

Я создал простой HttpClient с AFNetworking.iOS селектор отключает приложение

HttpClient *client = [[HttpClient alloc] initWithTarget:(id)target 
               before:(SEL)before 
               success:(SEL)success 
               failure:(SEL)failure 
               timeout:(SEL)timeout] 

Таким образом, контроллер может регистрировать функцию обратного вызова, когда он выполняет запрос HTTP. И вот как я написал функции обратного вызова:

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

Но здесь я нашел проблему. Когда запрос возвращается быстро, он работает нормально. Но если запрос возвращается после очень долгого времени, а между тем пользователь меняет вид. Затем он сбрасывает приложение и генерирует исключение, например, селектор не может быть выполнен на ноль-объекте.

Так что я хочу знать, сделал ли я это правильно и есть ли способ решить эту проблему? Каковы наилучшие методы реализации этого?

=====

Update:

К сожалению, я не ставил журнал ошибок. Но на самом деле нет большой информации, которую я могу получить. Иногда даже нет журнала. Я поставил здесь скриншот и надеюсь, что это может помочь.

Я только получаю это и нет журнала исключений.

enter image description here

==== Update

И надеюсь, что это может помочь

#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 

Спасибо всем большое!

==== Update

Я нашел проблему. Это было связано с тем, что я установил targe в assign вместо weak. Для разницы между assign и weak, пожалуйста, обратитесь к stackoverflow question

+0

Я предполагаю, что вы выполняете асинхронный запрос? – gikygik

+0

@gikygik Да, конечно. Некоторое представление требует, чтобы ответ возвращался первым. Таким образом, все функции просмотра чертежа помещаются в обратный вызов успеха. И другие предназначены для обработки ошибок. Но если я изменил представление до того, как запрос вернется, он потерпит крах. – Ciel

+0

Я предполагаю, что свойство successCallback присваивается, я прав? –

ответ

0

Не хватает информации о проблеме. Но я могу догадаться о возможной причине.

Я думаю, вы добавляете цель для первого запроса, она работает нормально, но когда вы делаете второй запрос, для селектора есть две цели. Но первый объект уже выпущен, поэтому он не может выполнять селектор.

Вы должны удалить цель, которая уже не используется.

+0

Но я действительно добавляю оператор if, чтобы проверить, нет ли цели и селектора или нет. Также я проверил, может ли цель перенести селектор или нет. Тогда почему это произойдет? – Ciel

0

Вам нужно использовать слабую ссылку для своей цели. Я не уверен, что ваша реализация выглядит, но это может дать вам подсказку:

__weak typeof(self) weakSelf = self; 
HttpClient *client = [[HttpClient alloc] initWithTarget:weakSelf 
               before:(SEL)before 
               success:(SEL)success 
               failure:(SEL)failure 
               timeout:(SEL)timeout] 

Другой, лучше ИМО, способ заключается в использовании блоков для AFNetworking доработок.

Смежные вопросы