近期在开发过程中,偶尔会发现工程里某个页面的通知(NSNotificationCenter)有时会被执行好几次(ps:在页面已经pop出栈之后继续执行,理论上出栈之后,页面应该被释放,通知在dealloc中也被移除)。通知的使用是在viewDidLoad方法里注册,在dealloc方法中移除。按理来说,在viewcontroller的生命周期里这两个方法都只会执行一次,是相对应的,所以不应该存在通知在页面出栈之后仍然被执行多次的情况。在排查后发现了原因:这个页面在被pop移出栈后没有被释放(即没有走dealloc方法)。
研究了一下,发现,是因为当前控制器被某个对象强引用了,控制器的引用计数不为0,系统无法帮你释放这部分内存,即dealloc方法没有走,通知没有移除。大致分为以下几种情况:
1.控制器中NSTimer没有被销毁
2.viewController中的代理不是weak属性
3.viewController中block的循环引用
最终发现是解除循环引用用的是__block修饰,所以导致了pop后页面无法释放(ps:工程为ARC环境)。
__block和__weak修饰符的区别:
1.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
2.__weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
3.__block对象可以在block中被重新赋值,__weak不可以。
4.__block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用,__weak只在ARC下使用,可以避免循环引用。
最终,把__block改成__weak。
如果block没有直接或者间接被self存储,就不会产生循环引用,就不需要用weak self。在block里有时直接用self之类的强引用对象时,而系统没有警告,这不代表不会造成对象无法释放的情况,要根据实际情况自我理解,不能完全按照理论,毕竟,实践出真知啊。
只要你在block里用到了self所拥有的东西,都有可能造成循环引用,一定要注意解除,也许并不会造成麻烦的结果,但作为一名开发人员,还是要精细,写高质量的代码,避免不必要的问题和时空损失!