内存泄漏可能存在在哪些地方

由于前段时间我负责的app 严重发热,由于上线时间短,所以出现了一些问题 结合网上一些教程我这边也仔细检查,检查的结果原因如下。可能有些地方写得不对,大神勿喷,请订正.传送门

1.Block循环引用问题

在苹果使用ARC管理之前,Block的内存管理(block如何进行内存管理的)需要区分是Global(全局)、Stack(栈)还是Heap(堆)。而在使用了ARC之后,苹果自动会将所有原本应该放在栈中的Block全部放到堆中,所以这使得我们现在的讨论可以省去很大一部分的麻烦。下面我们就只讨论ARC环境下全局Block和堆Block的内存管理。

Block的循环引用是比较容易被忽视,原本也是相对比较难检查出来的问题。当然现在苹果在XCode编译的层级就已经做了循环引用的检查,所以这个问题的检查就突然变的没有难度了。简单说一下循环引用出现的原理:Block的拥有者在Block作用域内部又引用了自己,因此导致了Block的拥有者永远无法释放内存,就出现了循环引用的内存泄漏

@interface ObjTest () {

NSInteger testValue;

}

@property (copy, nonatomic) void (^block)();

@end

@implement ObjTest

- (void)function {

self.block = ^() {

self.testValue = 100; 这里就会发生内存泄漏问题

? };

}

@end

ObjTest拥有了一个名字叫block的Block对象;然后在这个Block中,又对ObjTest的一个成员变量testValue进行了赋值。于是就产生了循环引用:ObjTest->block->ObjTest。

要避免循环引用的关键就在于破坏这个闭合的环。在目前只考虑ARC环境的情况下,笔者所知的只有一种方法可以破坏这个环:在Block内部对拥有者使用弱引用。

@interfaceObjTest () {

NSInteger testValue;

}

@property (copy, nonatomic)void(^block)();

@end

@implement ObjTest

- (void)function {

__weak ObjTest* weakSelf =self;

self.block= ^() {weakSelf.testValue=100;

};

}@end

在Block外创建一个对于self的弱引用,然后在Block内引用self的地方全部使用这个弱引用。这样就使得Block内部不会对self本身做引用计数+1的操作。那样就可以打破循环引用的环了。


2.AFN框架和MJRefresh上下拉刷新(其实都是block引发的命案)

在封装网络请求类时需注意的是需要将请求队列管理者AFHTTPSessionManager声明为单例创建形式。对于该问题,AFNetWorking的作者在gitHub上也指出建议使用者在相同配置下保证AFHTTPSessionManager只有一个,进行全局管理,因此我们可以通过单例形式进行解决。下方展示部分核心代码:

然后在说MJRefresh(参考:传送门)

一般的我们是怎么使用MJRefresh 这个玩意的 看代码

self.tableView.mj_header = [MJRefreshNormalHeader headerWithrefreshingBlock:^{

self.page =1;? ? ? ?

[self.dataArr removeAllObjects];? ? ? ?

[self loadData];

}];

若在MJRefresh的执行Block中调用当前self或其所属属性,一定要注意循环引用问题。我们简单分析下MJRefresh为什么会造成循环引用问题:点击进入headerWithrefreshingBlock对应方法即可

#pragmamark- 构造方法

+ (instancetype)headerWithrefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock{? ?

MJRefreshHeader *cmp= [[selfalloc] init];

cmp.refreshingBlock= refreshingBlock;

return cmp;

}

这里仅有三行代码,无非就是创建了下拉刷新部分View然后返回,这里比较重要的是cmp.refreshingBlock = refreshingBlock;这一句,这里的refreshingBlock是属于MJRefreshHeader的强引用属性,最后header会成为我们自己tableView的强引用属性mj_header,也就是说self.tableView强引用header, header强引用refreshingBlock,如果refreshingBlock里面强引用self,就成了循环引用,所以必须使用weakSelf,破掉这个循环.如何解决直接看传送们.

3.大次数循环内存暴涨问题(你永远都不知道会存在这里的内存泄漏)

for(inti =0; i <100000; i++) {

?NSString *string= @"Abc";

string= [string lowercaseString];

string= [stringstringByAppendingString:@"xyz"]; ?//引起内存飙涨 ?

NSLog(@"%@",string);

}

该循环内产生大量的临时对象,直至循环结束才释放,可能导致内存泄漏,解决方法为在循环中创建自己的autoReleasePool(autoReleasePool都干了啥,这么?),及时释放占用内存大的临时变量,减少内存占用峰值。

for(inti =0; i < 100000; i++) {

@autoreleasepool{? ? ? ? ? ??

NSString *string= @"Abc";

string= [stringlowercaseString];string= [stringstringByAppendingString:@"xyz"];

NSLog(@"%@",string);}? ??

}

附、如何检测App的内存泄漏问题

1、借助Xcode自带的Instruments工具(选取真机测试)

2、简单暴力的重写dealloc方法,加入断点或打印判断某类是否正常释放。

3、通过Facebook出品的FBMemoryProfiler工具类进行检测,感兴趣的童鞋可进行了解。

4、更多关于如何进行app性能优化请看:微信读书 iOS 性能优化总结? ??MLeaksFinder简介

推荐阅读更多精彩内容