偶遇一个bug,环境描述如下:
1.UIView类ViewA,定义了协议ViewADelegate,ViewA类的一个对象aView的delegate设定为另一个UIView的对象bView,[bView addSubView:aVIew],aView为成员变量,在bView 的dealloc中进行的release
2.在aView里有采用延时调用[self performSelector:@selector(refresh) withObject:nil afterDelay:0.3f];
3.在refresh方法体中会调用aView的代理对象方法
bug产生条件:
对bView进行释放操作,当正在延时0.3秒执行中进行的,那么会导致refresh执行delegate respondsToSelector时候,导致野指针
解决方案一:
在delegate respondsToSelector加上并列关系delegate是否为nil的判断
结果:依然野指针宕机
原因:delegate即bView release但是未成为nil
接着这个问题继续解决:
在bView的dealloc中对aView release前机型aView.delegate = nil的操作
结果:程序对bView释放不宕机
有人说在aView的dealloc中执行delegate = nil也行?
实测不行,因为在bView的dealloc中执行aView release并不立即执行aView的dealloc方法【因为,aView是被addsubview到bView中的,引用计数为2】,所以,宕机问题依然可能产生
解决方案二:
就是将[self performSelector:@selector(refresh) withObject:nil afterDelay:0.3f];修改,不进行延时加载【我的app中其实可以不进行延时的】直接是self refresh调用。这样就不存在0.3的时间内对bView进行了释放
对bug修改的总结:
1.对于 - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;方法的理解不到位,刚开始以为是开启了一个子线程,其实不是,它是在当前线程中延时执行一个选择体方法,正确的理解是,先执行这个方法后的代码,当delay的时间到了,执行该方法。相当于启动了一个定时源给当前线程【当然是main thread了】下的runloop
2.对于内存释放的处理。需要注意方式方法,有时候需要从程序结构设计的角度考虑,看似很恶心的问题修改,可以换一下结构的思路就迎刃而解了,就想方案二的方式
3.当某个对象的引用计数清空,会立即调用它的dealloc方法
参考资料:
http://www.apkbus.com/android-138609-1-1.html
http://www.dreamingwish.com/dream-2012/ios-multithread-program-runloop-the.html