自欺欺人的使用 NSTimer 销毁
自欺欺人的使用 NSTimer 销毁
1.NSTimer是要加到runloop中才会起作用。
常见的创建timer方式
// 第一种方式
@property (nonatomic , strong) NSTimer *timer;
// 默认加入当前runloop的NSDefaultRunLoopMode
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:NO];
// 第二种方式
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
第一种缺陷如果当前线程就是主线程,也就是UI线程时,某些UI事件,比如UIScrollView的拖动操作,会将Run Loop切换成
NSEventTrackingRunLoopMode模式,在这个过程中,默认的NSDefaultRunLoopMode模式中注册的事件是不会被执行的。也就是说,此时使用scheduledTimerWithTimeInterval添加到Run Loop中的Timer就不会执行第二种方式需要使用NSRunLoop的addTimer:forMode:方法来把Timer按照指定模式加入到Run Loop中。这里使用的模式是:
NSRunLoopCommonModes,这个模式等效于NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的结合
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
- 以上两种方式都是在主线程上创建的,如果在子线程创建的timer,加入到runloop则需要手动开启runloop
[[NSRunLoop currentRunLoop] run];,同时也必须在子线程销毁。
2.NSTimer会强引用它的target对象。
[self.timer invalidate]是唯一的方法将定时器从循环池中移除
- (void)dealloc
{
// 自欺欺人的写法,永远都不会执行到,除非你在外部手动invalidate这个timer
[self.timer invalidate];
}
当我们在控制器中创建timer且tager设为self时。
会发生 timer 添加到 Runloop 的时候,且会被 Runloop 强引用,
- Note in particular that run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.
然后 Timer 又会有一个对 Target 的强引用(也就是 self )
- Target is the object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated.
也就是说 NSTimer 强引用了 self ,self的全局变量 NSTimer 又使 self 强引用了 NSTimer,导致 self 一直不能被释放掉,所以也就走不到 self 的 dealloc 里。
此时我们就会想把 Target 设置为 weakSelf ,运行后也不起作用. 是由于我们的 self 和 weakSelf 都是指针指向控制器,控制器的dealloc需要timer的销毁才调用。同样造成相互强引用。
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(timerAction:) userInfo:nil repeats:NO];

- 此时我们又想到下面那种把timer设为weak,此时是直接运行造成坏内存访问,因为timer创建就销毁
@property (nonatomic , strong) NSTimer *timer;
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
- 另一种当我们在创建完timer后 置为nil,NSTimer还会不会起作用,答案是会起作用。因为Runloop对NSTimer 有了强引用,指向NSTimer那块内存。
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
self.timer = nil;
### 3.解决runloop循环引用
- iOS 10.0 以后增加两个创建方法
(NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
##### 自定义分类创建NSTimer,适用于iOS 10以前
- 原理等同于以上方法,把 target 转换为 NSTimer 自身然后把控制器的定时器方法在block方法中保存执行。
(instancetype)syl_timerWithTimeInterval:(NSTimeInterval)time repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
{
// 此时的 target 为 NSTimer
return [NSTimer timerWithTimeInterval:time target:self selector:@selector(timeAction:) userInfo:block repeats:repeats];
}(void)timeAction:(NSTimer *)timer {
void (^block)(NSTimer *) = [timer userInfo];
!block?:block(timer);
}
### 4.使用 GCD 定时器
// GCD 定时器
- (void)timerNine {
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = dispatch_queue_create("SYLingGCDTimer", DISPATCH_QUEUE_CONCURRENT);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// leewayInSeconds 精准度
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
// code to be executed when timer fires
timer;
[weakSelf timerAction];
});
dispatch_resume(timer);
}
[Demo地址](https://github.com/SYLing/SYLTimer)
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1ritpp1xqps9u
自欺欺人的使用 NSTimer 销毁的更多相关文章
- NSTimer 销毁问题 和 iOS中控制器的释放问题
俗话说的好,前人栽树后人乘凉,最近看了很多博文,不少博文提到了NSTimer的销毁问题, 之前我都没怎么注意,现在对照着文章一一实践发现坑还真不少.下面是我读到的几篇博文分享给大家 @啸笑天的NSTi ...
- iOS之 NSTimer(一)
以前没怎么了解过这个NSTimer,其实还是有挺多坑的,今天来总结一下: 首先我们一起来看这个: 我在A -> (push) -> B控制器,然后再B控制器中开启了一个NSTimer.然 ...
- 控制器pop时没有被销毁(没有走dealloc方法)错误原因
ARC环境下,不需要我们进行过多的内存的管理我们需要做的就是在dealloc方法中进行内存管理,但是错误的代码也会造成内存管理方法dealloc不执行,错误的原因无非以下三种,其中第二种和第三种最容易 ...
- iOS,视图控制器相关(UIViewController)
1.视图控制器各个方法调用时机 2.选项卡(Tab Bar)和导航栏(Navigation Bar) 3.有无控制器的页面跳转 4.页面跳转隐藏底部选项卡 5.获取导航栏和状态栏高度,隐藏导航栏返回按 ...
- iOS中控制器的释放问题
iOS中控制器的释放问题 ARC工程是可以重写dealloc方法并被系统调用的,但不需要手动调用父类的dealloc,手写[super dealloc]方法会报错,事实上系统会自动帮你调用父类的dea ...
- 一览Swift中的常用关键字
要学习Swift这门语言,就必须先了解Swift的关键字及对应的解释.这里就列一下在Swift中常用到的关键字. 关键字是类似于标识符的保留字符序列,除非用重音符号(`)将其括起来,否则不能用作标识符 ...
- Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用
OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...
- ios - NSTimer中target的self是强引用问题
当控制器ViewController跳转进入控制器OneViewController中的时候开启定时器,让定时器每隔一段时间打印一次,当OneViewController dismiss的时候,控制器 ...
- ios基础篇(二十三)—— 定时器NSTimer与图片的自动切换
一.NSTimer NSTimer是一个能在从现在开始到后面的某一个时刻或者周期性的执行我们指定的方法的对象.可以按照一定的时间间隔,将制定的信息发送给目标对象.并更新某个对象的行为.你可以选择在未来 ...
随机推荐
- Go语言的管道Channel用法
本文实例讲述了Go语言的管道Channel用法.分享给大家供大家参考.具体分析如下: channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值. ch <- ...
- java nio的一个严重BUG
java nio的一个严重BUG Posted on 2009-09-28 19:27 dennis 阅读(4588) 评论(5) 编辑 收藏 所属分类: java .源码解读 这个BU ...
- Windows命令行bat批处理延迟sleep方法
使用ping 的定时功能,精度1秒 实战:创建示例文件test.bat,内容如下: 代码如下:ping -n 3 127.0.0.1>nul 说明:3为ping包发送次数,可作为延迟秒数进行使用 ...
- Hadoop学习资料(持续更新)
Alex的Hadoop菜鸟教程 Hadoop资料合集 Hadoop平台和应用程序框架
- EF提高性能
实体框架 5 性能注意事项 作者:David Obando.Eric Dettinger 等 发布时间:2012 年 4 月 1.简介 对象关系映射框架是一种在面向对象的应用程序中提供数据访问抽象的便 ...
- centos查看CPU的数量
# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 # 查看物理CPU个数 cat /proc/cpuinfo| ...
- hdoj4180
题意: 使(a/b-c/d)最小,然后让你求c/d. 我们能说最小the error |A/B - C/D| 然后C,D的范围是 0 < C < D < B. 其实就是:求接近(A/ ...
- python 类属性和实例属性、方法 访问权限问题
class Animal: cls_attr = 'cls_attr' _cls_attr = '_cls_attr' __cls_attr = '__cls_attr' def __init__(s ...
- bzoj 2959: 长跑【LCT+并查集】
如果没有环的话直接LCT 考虑有环怎么办,如果是静态的话就tarjan了,但是这里要动态的缩环 具体是link操作的时候看一下是否成环(两点已联通),成环的话就用并查集把这条链缩到一个点,把权值加给祖 ...
- bzoj 4407: 于神之怒加强版【莫比乌斯反演+线性筛】
看着就像反演,所以先推式子(默认n<m): \[ \sum_{d=1}^{n}d^k\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==d] \] \[ =\sum_{d=1} ...