先貼上此場景中的三處代碼(此代碼已解決該bug)仔戈,場景及方法為中間注釋部分:
//輪播圖中的代理方法
- (void)carouselCurrentItemIndexDidChange:(iCarousel *)carousel {
//翻頁指示器
self.pageControl.currentPage = carousel.currentItemIndex;
//每次翻頁改變時寻馏,就重置定時器
[self timerFire];
}
#pragma mark 定時器啟動
- (void)timerFire
{
[self.timer invalidate];
/**
* 這里用_portrait來間接判斷self是否被釋放篇恒,原因如下:
* 此處發(fā)現(xiàn)一個bug媒咳。
* 當(dāng)前界面為A,這處代碼也都在A界面中使碾, 從A push界面B蜜徽,在B中發(fā)網(wǎng)絡(luò)請求添加了驗證當(dāng)網(wǎng)絡(luò)請求返回code=-2時,在B中popToRootViewController 然后切換根視圖票摇,
* 控制器A還沒來得及銷毀拘鞋,A中輪播圖滾動時觸發(fā)方法[carouselCurrentItemIndexDidChange:]中的方法[self timerFire],
* 執(zhí)行完 timerFire方法中的 [self.timer invalidate]后矢门,立馬進入dealloc方法盆色,銷毀對象,
* 再接著執(zhí)行 timerFire方法中的 [self.timer invalidate]后面的方法 __weak typeof(self) weakSelf = self;
* 但是此時 self已經(jīng)是被銷毀的對象祟剔,(注意:雖然self被銷毀傅事,但它此時還不為空!因此此時不能在程序中使用self峡扩,否則會報對象已被釋放的地址錯誤。但是通過觀察控制臺障本,self中的對象_portrait等已經(jīng)被釋放為空了教届。)
* 在百度了兩種方法之后(后面介紹)响鹃,都不可行,因為此時程序中不能使用self案训,也就不能使用 self.portrait买置,但是我發(fā)現(xiàn),可以使用 _portrait 强霎,
* (self.portrait是先調(diào)用getter方法獲值忿项,而_portrait是直接訪問對象的內(nèi)存地址來獲值)
* 因此,使用_portrait 判斷 _portrait是否為空城舞,來間接判斷 self是否被釋放轩触。(_portrait是viewDidLoad中創(chuàng)建的全局屬性,只要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執(zhí)行的方法
問:我有一個對象mytablecontroller,它有一個線程隊列queue拉馋,每次需要請求table的數(shù)據(jù)的時候就添加一個operation去訪問web榨为,再performSelectorOnMainThread進行reloadtable。然而很有可能mytablecontroller在執(zhí)行reloadtable方法前煌茴,用戶就執(zhí)行了導(dǎo)航切換了(navigationController popToViewController)随闺。這時mytablecontroller被release了,而operation子線程還在繼續(xù)執(zhí)行蔓腐,于是reloadtable就EXC_BAD_ACCESS了矩乐。我想給reloadtable添加一個判斷,判斷一下self.table是否被釋放合住。請問我應(yīng)該怎么做绰精?if(self.table!=nil)我試過了,行不通透葛,因為即使release了也不意味著就nil了笨使。
有人回答: mytablecontroller這個類的dealloc方法里面,要cancel這個queue
所以我想起了使用 performSelector: 去調(diào)用方法是可以調(diào)用cancelPreviousPerformRequestsWithTarget:selector:object:取消的僚害,詳情見此文 iOS: NSObject中執(zhí)行Selector的相關(guān)方法
試驗后發(fā)現(xiàn)不行硫椰,我覺得可能是因為要用延時afterDelay才有效果performSelector:withObject:afterDelay:
2. 如何檢測一個對象被釋放
關(guān)于這個問題,我百度和stackoverflow上都沒有找到可靠的答案萨蚕,現(xiàn)在來求助各位大神靶草。
網(wǎng)上普遍提到的一種方法是使用如下方式:
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測試都不可用
但是 我在注釋的場景中說了岳遥,這個對象當(dāng)前控制器self奕翔,而不是self中的屬性,dealloc后不能再程序中使用self浩蓉,所以此思路也無法使用派继。
總結(jié):
這個bug是在界面A push界面B宾袜,在B中發(fā)請求添加了驗證token=-2要退出切換根視圖時才遇到的,可能是我用定時器控制輪播圖滾動的邏輯不太好驾窟。