美文网首页
IOS NSTimer内存泄露,销毁,计数问题

IOS NSTimer内存泄露,销毁,计数问题

作者: 本客 | 来源:发表于2020-03-31 10:19 被阅读0次

今天闲了下来,记录一个特别棘手,又奇葩的问题,就是咱们常用的NSTimer计时器,我前几天在项目中遇到一个问题,就是NSTimer内存泄露等问题,我们很多人都喜欢在页面销毁的时候,把定时器在delloc中直接这样写

-(void)dealloc{

    //停止定时器

    [Timer invalidate];

    Timer=nil;

   }

甚至还有朋友这样写

-(void)dealloc{

    //停止定时器

    [Timer invalidate];

   }

这样写就是错,大错特错,先说说按照这两种方法来写,你可以看看你的dealloc绝对没有走,dealloc不走,这可是一个非常严重的内存泄露的问题,也是引起闪退和内存越来越大的问题,那么咱们继续探究,我试着把timer去掉

-(void)dealloc{

   }

这时我发现dealloc正常走了,是咱们写的停止NSTimer的代码有问题吗?

当然不是,因为scheduledTimerWithTimeInterval不仅仅是创建了NSTimer对象, 还把该对象加入到了当前的runloop中!

这和销毁NSTimer什么关系呢?我们先了解使用NSTimer时, ARC是怎么工作的?

首先, 是创建NSTimer, 加入到runloop后, 除了ViewController之外iOS系统也会强引用NSTimer对象,当调用invalidate方法时, 移除runloop后, iOS系统会解除对NSTimer对象的强引用, 当ViewController销毁时, ViewController和NSTimer就都可以释放了

当将NSTimer对象置nil时, 虽然解除了ViewController对NSTimer的强引用, 但是iOS系统仍然对NSTimer和ViewController存在着强引用关系

神马? iOS系统对NSTimer有强引用很好理解, 对ViewController本来不就是强引用么?

这里所说的iOS系统对ViewController的强引用, 不是指为了实现View显示的强引用, 而是指iOS为了实现NSTimer而对ViewController进行的额外强引用

综上所述, 销毁NSTimer的正确姿势应该是

-(void)dealloc{

    //停止定时器

    [Timer invalidate];// 真正销毁NSTimer对象的地方

    Timer=nil;// 对象置nil是一种规范和习惯

   }

对的,咱们的代码是没有错的,我解释了这么多就是为了证明这一点,那为什么不走dealloc方法呢?

别急,我们继续往下看

因为创建NSTimer的时候,他的计数为1,但是他被加到runloop中之后计数为2,说到这里有很多朋友大概都懂了,就算页面销毁NSTimer的时候,引用计数器也是为2,NSTimer不为nil,ios系统在ARC下,是不会重置内存的,所以这就导致了指向NSTImer内存的指针变成了野指针,最终内存泄露

所以我们要在其他生命周期方法里, 例如ViewWillDisappear中再次进行销毁,

-(void)viewDidDisappear:(BOOL)animated{

    [superviewDidDisappear:animated];

     if(Timer!=nil){

     [Timer invalidate];

     Timer=nil;

     }

这样咱们才是真正的销毁了NSTimer,也不会有任何内存泄露的问题。

可惜好景不长,有碰到一个问题,比如说咱们的定时器是一个短信验证码倒计时,当咱们点击倒计时按钮后,NSTImer开始跳动,正常的逻辑是如果该页面pop到上个界面,再push该界面,NSTImer重置;

如果是该界面push到下个界面,然后再pop回该界面,那么定时器应该是持续跳动,不会停;

但是结果一试不是这样的,不管是pop上个界面,再push该界面,还是push下个界面,再pop回该界面,前者是NSTimer被重置了,后者是NSTImer跳动数字停止在了push下个界面时候的那个数字,怎么办?这不是我们想要的效果呀,其实不难解决

因为push下个界面,周期只走了-(void)viewDidDisappear:(BOOL)animated; 而没有走-(void)dealloc

而pop上个界面,周期不仅走了-(void)viewDidDisappear:(BOOL)animated; 同时也走了-(void)dealloc

为了实现咱们想要的效果,可以在-(void)viewDidDisappear:(BOOL)animated; 中添加一个判断方法,判断是pop上个界面,还是push下个界面,代码这样写

-(void)viewDidDisappear:(BOOL)animated{

    [superviewDidDisappear:animated];

    //判断页面是pop到上个界面还是push到下个界面

    NSArray * viewControllers = self.navigationController.viewControllers;

    if(viewControllers.count>1&& [viewControllers  objectAtIndex:viewControllers.count-2] ==self){

    //push到下个界面,不销毁也不停止NSTimer

    }else{

   //pop到上个界面,销毁停止NSTimer

        if(Timer != nil){

            [Timer invalidate];

            Timer=nil;

        }

    }

}

这样就大功告成了,提醒大家一定要重视内存的问题,千里之堤毁于蚁穴,这不是小问题。

相关文章

  • IOS NSTimer内存泄露,销毁,计数问题

    今天闲了下来,记录一个特别棘手,又奇葩的问题,就是咱们常用的NSTimer计时器,我前几天在项目中遇到一个问题,就...

  • deinit方法检测内存泄露

    //(deinit未调用,则内存泄露) deinit{ print("对象销毁,没有内存泄露:\(self.cla...

  • 文章目录

    block block内强引用导致的内存泄露 NStimer UITableView滚动式NSTimer停止计数 ...

  • Android内存泄露解析

    内存泄露的原因是引用还存在,无法及时释放内存,android的内存泄露主要是activity销毁不及时。 造成内存...

  • 底层原理:内存管理

    定时器 下面这种用法会存在内存泄露 NSTimer用下面的方式也会引起内存泄露 而使用下面的方式则不会出现内存泄露...

  • ARC下内存泄露的几种情况

    delegate设为strong造成的内存泄露(两个对象相互强引用) NSTimer 造成的内存泄露(两个对象相互...

  • iOS NSTimer导致内存泄露问题

    通常情况下 初始化NSTimer 执行timer方法 最后销毁控制器的时候停止定时器并置空 然而当我们退出控制器的...

  • 防止内存泄露的NSTimer定时器

    目录 NSTimer的基础用法 NSTimer的内存泄露 安全防侧漏的定时器 NSTimer的基础用法 创建定时器...

  • 分享:在iOS上自动检测内存泄露

    分享:在iOS上自动检测内存泄露 分享:在iOS上自动检测内存泄露

  • 内存泄漏/管理

    ARC 下内存泄露的那些点performSelector延时调用导致的内存泄露iOS ARC下几种导致内存泄露的场...

网友评论

      本文标题:IOS NSTimer内存泄露,销毁,计数问题

      本文链接:https://www.haomeiwen.com/subject/mmiiuhtx.html