最近看一个老项目发现在某些部分在运行的时候会出现内存泄漏,造成内存每打开一次界面就会增长大概10几兆,所以我就着手查了一下问题,下面对查找以及解决的过程做了一下总结。
1. 首先凭着之前遇到的问题总结出来的经验,先从类中的属性或称之为变量或者调用方法在block中的循环引用问题,例如如下代码
[_photos enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj != [NSNull null]) {
if (!(preserveCurrent && obj == [self photoAtIndex:self.currentIndex])) {
[obj unloadUnderlyingImage];
}
}
}];
通过以上代码我们可以看到我们在block中用到了self的相关引用,一个方法调用和一个变量调用,那么这个时候回对当前类造成循环引用,从而使当前类释放不掉,那么遇到这种问题我们来如何应对呢,我们可以做如下修改
__weak __typeof(self)weakSelf = self;
[_photos enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj != [NSNull null]) {
if (!(preserveCurrent && obj == [weakSelf photoAtIndex:weakSelf.currentIndex])) {
[obj unloadUnderlyingImage];
}
}
}];
可以看出上面这段修改过的代码使用了__weak修饰词生成一个weak的一个self的软引用那么在block中使用这个生成的self的weak软引用就可以解决循环引用的问题,这个很容易理解就不必多说了,如果你的代码中也有这样的可以试着浏览一遍看有没有循环引用的情况。
2. Objective-C中在类中生命的private变量在block中的循环引用
往往我们在开发过程中会在类头里定义一些私有变量,这些变量在类的外部是不能直接引用的,可以说是隐藏,但这些变量如果直接拿到block中使用就会出现循环应用的问题,其实都是对当前类self的循环引用,这样的循环引用问题很难查找出来,下面看一段有问题的代码
@interface MyClass() : UIViewController <UIScrollViewDelegate, UIActionSheetDelegate> {
NSMutableArray *_datas;
}
[_photos enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj != [NSNull null]) {
if (!(preserveCurrent && obj == [self photoAtIndex:self.currentIndex])) {
[obj unloadUnderlyingImage];
_data.count;
}
}
}];
在block中直接使用这个时候self又出现了循环引用的情况,那么解决方案和self是一样的,直接做一个weak软引用转换就行,然后在block中使用软引用就能解决问题。具体写法和self差不多,只不过类型需要替换成对应的类型。
3. NSTimer没有正确停掉造成当前类不被释放
这个没什么好解释的,就是在使用NSTimer的时候需要在不使用它时将它停掉并且置空这样才能确保被用作target的类被正确释放从而不出现内训泄漏。
希望这篇文档能对踩坑的人有帮助,然而swift版本的类似,同样使用weak就搞定了,当然timer还是需要正确停止才可以避免内存泄漏的问题
如果有问题疑问可以直接留言
文中只是提到了内存泄漏的部分,但在for循环进行大数据处理的时候需要将循环体加到autorelease自动释放池里处理以免造成内存使用峰值突然非常大造成APP直接内存溢出从而crash