iOS选择器崩溃了应用程序

时间:2015-04-29 17:14:26

标签: ios objective-c

我使用AFNetworking创建了一个简单的HttpClient。

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

但在这里我发现了一个问题。当请求快速返回时,它工作正常。但是如果请求在很长一段时间后回来,同时用户会更改视图。然后它崩溃应用程序并抛出一个异常,例如,无法在nil对象上执行选择器。

所以我想知道我是否以正确的方式做到了,有没有办法解决这个问题?实现这一目标的最佳做法是什么?

=====

更新

抱歉,我没有输入错误日志。但实际上我没有太多的信息。有时甚至没有日志。我在这里放了一个截图,希望它可以提供帮助。

我只能得到这个,并且没有异常日志。

enter image description here

==== 更新

希望这可以提供帮助

#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

非常感谢你们!

==== 更新

我发现了问题。这是因为我将目标设置为assign而不是weak。有关assignweak之间的差异,请参阅stackoverflow question

1 个答案:

答案 0 :(得分:0)

您需要为目标使用弱自引用。 我不确定你的实现是什么样的,但这可能会给你一个提示:

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

另一种更好的IMO方式是使用块来进行AFNetworking完成。