在做内存检测的时候偶然发现使用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);
}];
}
这样处理内存警告应该还是会有的吧,只是不用担心内存泄露问题了。