先贴上此场景中的三处代码(此代码已解决该bug),场景及方法为中间注释部分:
//轮播图中的代理方法
- (void)carouselCurrentItemIndexDidChange:(iCarousel *)carousel {
//翻页指示器
self.pageControl.currentPage = carousel.currentItemIndex;
//每次翻页改变时,就重置定时器
[self timerFire];
}
#pragma mark 定时器启动
- (void)timerFire
{
[self.timer invalidate];
/**
* 这里用_portrait来间接判断self是否被释放,原因如下:
* 此处发现一个bug。
* 当前界面为A,这处代码也都在A界面中, 从A push界面B,在B中发网络请求添加了验证当网络请求返回code=-2时,在B中popToRootViewController 然后切换根视图,
* 控制器A还没来得及销毁,A中轮播图滚动时触发方法[carouselCurrentItemIndexDidChange:]中的方法[self timerFire],
* 执行完 timerFire方法中的 [self.timer invalidate]后,立马进入dealloc方法,销毁对象,
* 再接着执行 timerFire方法中的 [self.timer invalidate]后面的方法 __weak typeof(self) weakSelf = self;
* 但是此时 self已经是被销毁的对象,(注意:虽然self被销毁,但它此时还不为空!因此此时不能在程序中使用self,否则会报对象已被释放的地址错误。但是通过观察控制台,self中的对象_portrait等已经被释放为空了。)
* 在百度了两种方法之后(后面介绍),都不可行,因为此时程序中不能使用self,也就不能使用 self.portrait,但是我发现,可以使用 _portrait ,
* (self.portrait是先调用getter方法获值,而_portrait是直接访问对象的内存地址来获值)
* 因此,使用_portrait 判断 _portrait是否为空,来间接判断 self是否被释放。(_portrait是viewDidLoad中创建的全局属性,只要self没被销毁,_portrait就不会为nil)。
*/
if (_portrait) {
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer bk_scheduledTimerWithTimeInterval:4 block:^(NSTimer *timer) {
[weakSelf.carousel scrollToItemAtIndex:_carousel.currentItemIndex+1 animated:YES];
} repeats:YES];
}
}
- (void)dealloc {
[self.timer invalidate];
BLYLogDebug(@"销毁 PersonalInfoVC");
}
下面说一下我百度到的两种方法
1. 取消performSelector执行的方法
问:我有一个对象mytablecontroller,它有一个线程队列queue,每次需要请求table的数据的时候就添加一个operation去访问web,再performSelectorOnMainThread进行reloadtable。然而很有可能mytablecontroller在执行reloadtable方法前,用户就执行了导航切换了(navigationController popToViewController)。这时mytablecontroller被release了,而operation子线程还在继续执行,于是reloadtable就EXC_BAD_ACCESS了。我想给reloadtable添加一个判断,判断一下self.table是否被释放。请问我应该怎么做?if(self.table!=nil)我试过了,行不通,因为即使release了也不意味着就nil了。
有人回答: mytablecontroller这个类的dealloc方法里面,要cancel这个queue
所以我想起了使用 performSelector: 去调用方法是可以调用cancelPreviousPerformRequestsWithTarget:selector:object:取消的,详情见此文 iOS: NSObject中执行Selector的相关方法
试验后发现不行,我觉得可能是因为要用延时afterDelay才有效果performSelector:withObject:afterDelay:
2. 如何检测一个对象被释放
关于这个问题,我百度和stackoverflow上都没有找到可靠的答案,现在来求助各位大神。
网上普遍提到的一种方法是使用如下方式:
UIView *myView = [[UIView alloc] init];
Class oldClass = object_getClass(myView);
[myView release];
Class newClass = object_getClass(myView);
if(oldClass == newClass){
//not released
}
else {
//released
}
在iOS8.3上测试可用,在iOS6和iOS7测试都不可用
但是 我在注释的场景中说了,这个对象当前控制器self,而不是self中的属性,dealloc后不能再程序中使用self,所以此思路也无法使用。
总结:
这个bug是在界面A push界面B,在B中发请求添加了验证token=-2要退出切换根视图时才遇到的,可能是我用定时器控制轮播图滚动的逻辑不太好。