前言:今天同事突然跟我说,工程里有很多控制器并未销毁,关于这个问题我一直没太在意,就趁着有时间就查了查资料然后进行修改和测试,得到了以下结论!
如何查看控制器是否销毁,我们在 ARC 模式下可以重写 dealloc 方法,可以打个断点或者 NSLog 一下,如果未执行就说明当前控制器未被销毁,或许如果你想查看当前控制器的引用计数可以在视图将要消失的方法中打个断点,在控制台执行 po self.retainCount 就会看到当前控制器被引用的计数了,当然在这里打印实际上是不准的!!!
控制器在被pop后移出栈后会被释放,但有些时候会发现控制器出栈的时候不会调用dealloc方法,归根结底,是因为当前控制器被某个对象强引用了,控制器的引用计数不为0,系统无法帮你释放这部分内存。
注:重写 deallocd方法不需要手动调用父类的dealloc,手写[super dealloc]方法会报错,事实上系统会自动帮你调用父类的dealloc方法,不需要你实现。可以通过在dealloc方法中打印log查看控制器是否被释放。
控制器未被销毁,基本上就是三种原因:
- 控制器中block的循环引用
block会把它里面的所有对象强引用(在ARC下)/PS:MRC下会retain加1/,包括当前控制器self,因此有可能会出现循环引用的问题。
即一个对象有一个Block属性,然而这个Block属性中又引用了对象的其他成员变量,那么就会对这个变量本身产生强应用,那么这个对象本身和他自己的Block属性就形成了循环引用。在ARC下需要修改成这样:(/也就是生成一个对自身对象的弱引用/)
__weak typeof(self) weakSelf = self;
即:保险起见block中所有的涉及到self的全给替换成weakSelf
以下是我工程中出现的问题,同仁们也可以在自己的项目中从以下几点出发查看
1.在 block 内部无论是属性或者是实例变量都会在 block 内部被强引用,所以在检查时要注意内部的属性或者实例变量是否进行了弱引用,我目前的解决办法是将实例变量改为了属性!
2.在系统编译时,虽然我们未执行 block 的内部代码,但是编译器在编译时都会默认读取的,所以在项目中你认为很多未执行或写在 return 后面你认为不会执行的代码系统都会编译的,尽量做到不用的代码删除掉!
3.如果项目中我们使用了自定义的视图(例如 cell),如果在自定义的视图中引用了某个控制器,我们要将控制器进行一次弱引用,避免出现循环引用的问题!
- 控制器中的代理不是weak属性
例如@property (nonatomic, weak) id<CustmViewDelegate> delegate;代理要使用弱引用,因为自定义控件是加载在视图控制器中的,视图控制器view对自定义控件是强引用,
如果代理属性设置为strong,则意味着delegate对视图控制器也进行了强引用,会造成循环引用。导致控制器无法被释放,最终导致内存泄漏。
- 控制器中NSTimer没有被销毁
当控制器中存在NSTimer时,就需要注意,因为当[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];时,这个/target:self/ 就增加了VC的RetarnCountr, 如果你不将这个timer invalidate,就别想调用dealloc。需要在viewWillDisappear之前需要把控制器用到的NSTimer销毁。
[timer invalidate]; // 销毁timer
timer = nil; // 置nil
这篇文章我主要是说一下在项目中出现的问题,主要还是 block 内部未对属性进行弱引用,文章中主要参考了[https://segmentfault.com/a/1190000003858306] 这篇文章,并且 copy 了许多并结合我在项目中出现的问题,本人才疏学浅,文章中有不当的地方还请各位同仁指正,并一起交流,十分感谢!
网友评论