使用AFNetworking检测出内存泄漏问题探究

在做内存检测的时候偶然发现使用AF造成内存泄露问题。是什么原因造成的内存泄露呢?怎么解决?

快速解决办法

将AFHTTPSessionManager设置成全局单例

AFHTTPSessionManager单例:

+ (AFHTTPSessionManager*) manager
{
    static dispatch_once_t onceToken;
    static AFHTTPSessionManager *manager = nil;
    dispatch_once(&onceToken, ^{
        manager = [AFHTTPSessionManager manager];
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/json", @"text/javascript",@"text/html",@"text/plain",nil];
        manager.requestSerializer=[AFHTTPRequestSerializer serializer];
        manager.requestSerializer.timeoutInterval = 10;
    });
    return manager;
}

POST请求

+ (void)postWithUrlString:(NSString *)urlString parameters:(NSDictionary *)parametersDic completionHandler:(void (^)(NSString * _Nullable, NSDictionary * _Nullable))handle{
    
    [[self manager] POST:urlString parameters:parametersDic progress:^(NSProgress * _Nonnull uploadProgress) {
        
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if ([responseObject isKindOfClass:[NSDictionary class]]) {
            NSDictionary *resultDic = (NSDictionary *)responseObject;
            handle(nil,resultDic);
        }else{
            handle(@"数据格式异常",nil);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        handle(error.description,nil);
    }];
}

原因

首先抛出两个问题:

(1)是什么原因造成的内存泄露问题?

(2)为什么将AFHTTPSessionManager设置成全局单例就不会有内存警告了?

AFHTTPSessionManager继承自AFURLSessionManager

AFURLSessionManager里有个session属性

/**
 The managed session.
 */
@property (readonly, nonatomic, strong) NSURLSession *session;

AFURLSessionManager创建了session并将session的delegate设置为了自己

self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

NSURLSession是一个单例

/*
 * The shared session uses the currently set global NSURLCache,
 * NSHTTPCookieStorage and NSURLCredentialStorage objects.
 */
@property (class, readonly, strong) NSURLSession *sharedSession;

NSURLSession里有一个delegate

@property (nullable, readonly, retain) id <NSURLSessionDelegate> delegate;

问题就出在这里了,这个delegate为什么不是weak的?而是用了retain。

NSURLSession为了使网络请求回来之前session的delegate对象不被释放所以没有使用weak修饰符?为什么要这样呢?

也就是说一个AFHTTPSessionManager实例,发送一次网络请求后,当网络请求没回来之前AFHTTPSessionManager实例是不会被释放的,因为AFHTTPSessionManager作为session的delegate,是被session强引用的。所以检测内存会报红色警告。

思考

将AFHTTPSessionManager设置成全局的单例是规避掉了内存警告爆红的问题,已经是单例了,也就无作为释放实例了,所以检测内存时不会爆红。这样做也是合理的,毕竟一个移动端项目里网络请求是不可缺少了,没了HTTPS,移动端是玩不转的。与其每次请求都创建一次AFHTTPSessionManager的实例,不如将AFHTTPSessionManager设计成一个单例,提供全局使用,也避免了每次创建造成内存分片的问题。

将内存问题规避掉只是一种解决办法,还应该有其他办法也能解决这个问题。

问题不就是AFHTTPSessionManager释放不了造成的吗,在AFHTTPSessionManager收到session的回调之后,将session的delegate强制置为nil,然后在引用AFHTTPSessionManager实例的类中收到回调后将AFHTTPSessionManager实例置空,这算不算另一种解决办法呢,这种解决办法比较中规中矩,不优,比较麻烦,但也是一种思路。

网上也有小伙伴说使用NSURLSession进行请求后,等请求完毕后调用session的finishTasksAndInvalidate方法,或者调用取消session的
invalidateAndCancel方法,再或者将session属性置成nil,这样AFURLSessionManager就能正常释放,如下:

- (AFHTTPSessionManager *)getSessionManager{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html",@"text/json", @"text/plain", @"text/javascript",@"text/xml", nil];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    return manager;
}

POST请求

+ (void)postWithUrlString:(NSString *)urlString parameters:(NSDictionary *)parametersDic completionHandler:(void (^)(NSString * _Nullable, NSDictionary * _Nullable))handle{
    AFHTTPSessionManager *manager = [self getSessionManager];
    __weak typeof(manager)weakManager = manager;
    [weakManager POST:urlString parameters:parametersDic progress:^(NSProgress * _Nonnull uploadProgress) {
        
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
         __strong typeof (weakManager)strongManager = weakManager;
        [strongManager invalidateSessionCancelingTasks:YES];

        if ([responseObject isKindOfClass:[NSDictionary class]]) {
            NSDictionary *resultDic = (NSDictionary *)responseObject;
            handle(nil,resultDic);
        }else{
            handle(@"数据格式异常",nil);
        }
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

        __strong typeof (weakManager)strongManager = weakManager;
        [strongManager invalidateSessionCancelingTasks:YES];

        handle(error.description,nil);
    }];
}

这样处理内存警告应该还是会有的吧,只是不用担心内存泄露问题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Morris_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值