今天在开发过程中,发现某个类里的通知(NSNotificationCenter)有时会被执行好几次。通知的使用是在viewDidLoad方法里注册,在dealloc方法中移除。按理来说,在viewcontroller的生命周期里这两个方法都只会执行一次,是相对应的,所以不应该存在通知被执行多次的情况。在排查后发现了原因:这个页面在被pop移出栈后没有被释放(即没有走dealloc方法)。
百度了pop移出栈后没有被释放的原因,归根结底,是因为当前控制器被某个对象强引用了,控制器的引用计数不为0,系统无法帮你释放这部分内存。大致分为以下几种情况
1.控制器中NSTimer没有被销毁
2.viewController中的代理不是weak属性
3.viewController中block的循环引用
对照以上情况和工程实际情况,前两种那个页面没有使用到,所以开始排查第三种情况。最终发现是解除循环引用用的是__block修饰,所以导致了pop后页面无法释放。
__block本身并不能避免循环引用,避免循环引用需要在block内部把__block修饰的obj置为nil
__weak可以避免循环引用,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题
__block和__weak修饰符的区别:
1.__block对象可以在block中被重新赋值,__weak不可以。
2._block会持有该对象,即使超出了该对象的作用域,该对象还是会存在的,直到block对象从堆上销毁;而__weak仅仅是将该对象赋值给weak对象,当该对象销毁时,weak对象将指向nil;
3.__block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
4.__weak只能在ARC模式下使用,也只能修饰对象,不能修饰基本数据类型。
5.__block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用,__weak只在ARC下使用,可以避免循环引用。
如果block没有直接或者间接被self存储,就不会产生循环引用,就不需要用weak self。在block里有时直接用self之类的强引用对象时,而系统没有警告,这不代表不会造成对象无法释放的情况。
只要你在block里用到了self所拥有的东西,都有可能造成循环引用,一定要注意!!!!!!!