iOS 控制器被释放了定时器却还在执行其中方法的bug

先贴上此场景中的三处代码(此代码已解决该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要退出切换根视图时才遇到的,可能是我用定时器控制轮播图滚动的逻辑不太好。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,824评论 0 9
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,219评论 30 472
  • 去年(2015年)年末的时候,我在壹咖啡遇到过一位大叔。 目测40多岁,发福的身材,油光可鉴的面庞,手提公文包,俨...
    努力的陈小白阅读 261评论 0 2
  • 书单收藏了一大推,亚马逊和当当网上各大图书榜单也浏览了不下十次,微信、微博、简书上凡是有关“如何挑书”、“如何读...
    lily不著名阅读 340评论 0 1
  • 一、人类为什么要争斗? 在没有长矛、枪炮、炸弹的为动物世界中,暴力的基本形式—— 动物之所以要争斗,原因在于 1、...
    程宇Alex阅读 851评论 0 0