iOS — NSTimer&CADisplayLink 销毁处理

释放双眼,带上耳机,听听看~!

都知道在使用NSTimer&CADisplayLink的时候没有正确处理就会导致控制器和定时器都不能释放。

//开启定时器
[self.timer fire];

//取消定时器(这个是永久的停止,对象释放)
[self.timer invalidate];
self.timer = nil;
注意:停止后,一定要将timer = nil,否则还是没有释放

//暂停定时器(对象没有释放,需要的时候可以再次开启)
self.timer.fireDate = [NSDate distantFuture];

//再次开启定时器
self.timer.fireDate = [NSDate distantPast];

相互引用的过程理解

网上很多人说控制器和定时器不能释放,是因为循环引用。并且说明循环引用过程,大概就是self.timer导致控制器强引用timer,并且创建timer的时候target强引用self,timer强引用target。

事实上并不是这个样子,通过测试发现,不管timer是局部变量还是属性变量只要timer的repeats为NO,在退出控制器的时候都可以正常释放,而repeats为YES的时候,就不能正常释放。而repeats为NO或者YES的区别就是系统默认把timer加入mainloop中了。所以真正原因应该是mainloop对timer的强引用。所以我认为 mainloop -> timer -> self 导致如果不做销毁处理就会控制器和定时器都不能释放

从retainCount上面来讲,self.timer被mainloop和self强应用,mainloop使timer retainCount +n,self 使timer retainCount +1,所以这是timer.retainCount = n+1;

这样就容易理解销毁操作了
[self.timer invalidate]; 解除mainloop对timer的强引用
self.timer = nil; 解除self对timer的强引用
timer被释放后,timer对self也就没有强引用了。

以上是我对这个过程的理解,如果有不对的地方可以相互探讨。

如何处理销毁操作?

最大错误是想当然的认为在delloc里面做销毁处理,控制器不销毁,会走delloc吗?

操作一:大部分人的做法,在viewDidDisappear做销毁处理,那么在pop到这个控制器的时候就没法启用这个定时器了;

操作二:有操作一想到,在self push 到 nextController的时候不要销毁处理,在self pop 出去的时候做销毁处理。

- (void)didMoveToParentViewController:(UIViewController *)parent {
         if (!parent) {
              [self.timer invalidate];
              self.timer = nil;
          }
}

当从一个视图控制容器中添加或者移除viewController后,该方法被调用。
在push添加进去的时候parent != nil,pop移除的时候parent = nil,于是上面的操作可行。

注意:模态present 、dismiss的时候不会调用,该方法不可行。

操作三:也就是YYKit里面YYFPSLabel使用的方法,YYWeakProxy。
它的原理就是在原来mainloop -> timer -> self 的强引用中添加一个target使它弱引用self ,切断timer和self之间的强应用。mainloop ——> timer ——> target —–> self。关键点就是target弱引用self 。

人已赞赏
iOS文章

Assertion failure in -[UICollectionViewData validateLayoutInRect:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.7.47/UICollectionViewData.m:433

2019-10-15 8:25:32

iOS文章

iOS 自定义弹出框

2019-10-15 10:09:47

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索