最近跳槽一家新公司,接手了以前的一个4年的老项目,传言中间有4个以上的人接手过,这中间代码质量也是没有任何把控的,所以为了提升系统健壮性,在完成项目新需求的同时进行着性能的优化,最近可能更多的记录一些优化方面的东西,这篇文章展示两个内存的泄露的例子:
1.NSDate
NSDate *date = [[NSDate alloc] init];
date = [date initWithTimeIntervalSince1970:42];
NSDate *date = [NSDate date];
date = [date initWithTimeIntervalSince1970:42];
很多人在创建date 不注意这一点,上述两种方式创建方式都会造成内存泄露,我们通过instruments 定位到泄露情况:
而 产生错误的原因很简单,新生成一个 date 对象,用date 指向生成的这个对象,此时这个对象的引用计数为1, 然后[date initWithTimeIntervalSince1970:42]
这个也会生成一个新的对象,date 指向这个对象,这个对象引用计数也为1,当出了作用域,date销毁了,第二次产生对象也随之销毁,而第一次产生的对象是无法销毁的,引用计数始终为1.
所以我们通常正确的创建的方式是生成并持有一个对象,避免产生多个无法访问的对象
NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970:42]
这样就不会产生内存泄露。
2.AFNetworking 使用问题
很多人在使用 AFNetworking 时候可能没有关注过这个问题,错误的使用AFNetworking 进行网络请求而导致内存泄露。
- (void)GET:(NSString *)url
parameters:(NSDictionary *)param
success:(void (^)(id _Nullable requestData))success
failure:(void (^)(NSError * _Nullable error))failure{
AFHTTPSessionManager *operationManage = [AFHTTPSessionManager manager];
operationManage.requestSerializer = [AFJSONRequestSerializer serializer];
operationManage.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManage GET:url parameters:param progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
有的人不注意这个细节,每次请求都会重新创建一个 AFHTTPSessionManager
, 这样就会导致内存泄露,具体的大家可以通过工具查看一下泄露的产生,我查看了一下网上,大家的通常说封装成一个单例就好了,很多人可能不太了解这个原因,以及为什么要这样做,我大概解释一下,大家就明白了这个原因
首先AFHTTPSessionManager
我们通过 manager
获取其实例的, 并且 是继承于 AFURLSessionManager
, 而 AFURLSessionManager
中一个会话对象NSURLSession *session
,而这个对象创建时候有一个代理属性是 retain
,当你重新再发一次网络请求时候,上一次NSURLSession *session
和 delegate
产生循环引用无法被释放了,除非你主动调用一下 '[mgr.session invalidateAndCancel]',这样才可以释放掉。
这样你就很容易理解内存泄露问题的根源了,解决起来就容易了
解决方案一
通过网上大众做法封装成一个单例,获取封装成一个static 变量在网络封装类initialize
中初始化, 当然这种方式你得确保你URL和参数设置的请求头是一致的,如果不一致,这样做肯定是不可以的。
解决方案二
每次请求结束主动调用一次取消
[operationManage.session invalidateAndCancel];
这样就能够解决了这种内存泄露。