最近项目突然被说会闪退,且无法重现,时有出现。接到问题后一步步排查,终于发现问题。
表现:某个页面的通知观察者方法被多次调用!
原因查找:
1、通知的监听这里我写在viewDidLoad里面,多次被调用只能说明多次发送了通知,或者多次添加了观察者。
2、通知的发送确定没多次调用,而通知的监听为何会被多次添加?
3、一般我们会在dealloc方法里面移除通知,打断点发现,dealloc方法并未调用!
4、通知每进一次页面添加一次,pop之后又未被移除,那就可能造成内存泄露,闪退!
解决思路:也就是要确定dealloc方法为何不走!
dealloc不被调用的原因:
1、ViewController中存在定时器NSTimer,定时器一跑起来就增加了ViewController的return count,如果你不将这个timer invalidate,则控制器无法释放;
2、ViewController中有强引用代理(delegate),改为weak修饰;
3、ViewController中有Block,且有循环引用。这个也是我不能调用dealloc的真正原因。Block体内使用实例变量会造成循环引用,使得拥有这个实例的对象不能释放。
例如你这个类叫OneViewController,有个属性是NSString *name; block的持有者也是VC的一个属性,如果你在block体中使用了self.name,那样子的话这个类就没法释放。
注意:VC持有的实例变量和@implementation下面创建的全局变量也会被循环引用!
@interface GroupChpPay () { UIView *_xxView; }
@implementation GroupChpPay { UIView *_xxView; }
即使在你的block代码中没有显式地出现"self",也会出现循环引用!就像上面的实例变量和全局变量,并不会使用self.调用,其实也会被循环引用。只要你在block里用到了self所拥有的东西!
但对于这种情况,我们无法通过加__weak声明或者__block声明去禁止block对self进行强引用或者强制增加引用计数。但我们可以通过指针来避免循环引用,具体是这么做的:
__weak typeof(self) weakSelf = self; self.blkA = ^{ __strong typeof(weakSelf) strongSelf = weakSelf;//加一下强引用,避免weakSelf被释放掉 NSLog(@"%@", strongSelf->_xxView); //不会导致循环引用. };
排查方法:
1、在添加通知的页面搜索所有 ^ 符号,也就是排查所有block方法有没有使用弱引用。基本都改好之后,dealloc方法可以被调用了,控制器销毁通知成功移除,问题解决!
2、真找不到哪里出错,最笨的方法,将可能出错的代码一段段注释排查!
还有值得一提的是,一般编译器会对需要使用weak弱引用的地方给出警告,但我这里没有警告,是在环信的SDK写的类里面,坑。