自欺欺人的使用 NSTimer 销毁

Demo地址

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,这个模式等效于NSDefaultRunLoopModeNSEventTrackingRunLoopMode的结合

[[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];
![](https://images2018.cnblogs.com/blog/720299/201804/720299-20180416224500403-1023535764.png)

- 此时我们又想到下面那种把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 销毁的更多相关文章

  1. NSTimer 销毁问题 和 iOS中控制器的释放问题

    俗话说的好,前人栽树后人乘凉,最近看了很多博文,不少博文提到了NSTimer的销毁问题, 之前我都没怎么注意,现在对照着文章一一实践发现坑还真不少.下面是我读到的几篇博文分享给大家 @啸笑天的NSTi ...

  2. iOS之 NSTimer(一)

    以前没怎么了解过这个NSTimer,其实还是有挺多坑的,今天来总结一下: 首先我们一起来看这个: 我在A  -> (push) -> B控制器,然后再B控制器中开启了一个NSTimer.然 ...

  3. 控制器pop时没有被销毁(没有走dealloc方法)错误原因

    ARC环境下,不需要我们进行过多的内存的管理我们需要做的就是在dealloc方法中进行内存管理,但是错误的代码也会造成内存管理方法dealloc不执行,错误的原因无非以下三种,其中第二种和第三种最容易 ...

  4. iOS,视图控制器相关(UIViewController)

    1.视图控制器各个方法调用时机 2.选项卡(Tab Bar)和导航栏(Navigation Bar) 3.有无控制器的页面跳转 4.页面跳转隐藏底部选项卡 5.获取导航栏和状态栏高度,隐藏导航栏返回按 ...

  5. iOS中控制器的释放问题

    iOS中控制器的释放问题 ARC工程是可以重写dealloc方法并被系统调用的,但不需要手动调用父类的dealloc,手写[super dealloc]方法会报错,事实上系统会自动帮你调用父类的dea ...

  6. 一览Swift中的常用关键字

    要学习Swift这门语言,就必须先了解Swift的关键字及对应的解释.这里就列一下在Swift中常用到的关键字. 关键字是类似于标识符的保留字符序列,除非用重音符号(`)将其括起来,否则不能用作标识符 ...

  7. Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用

    OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...

  8. ios - NSTimer中target的self是强引用问题

    当控制器ViewController跳转进入控制器OneViewController中的时候开启定时器,让定时器每隔一段时间打印一次,当OneViewController dismiss的时候,控制器 ...

  9. ios基础篇(二十三)—— 定时器NSTimer与图片的自动切换

    一.NSTimer NSTimer是一个能在从现在开始到后面的某一个时刻或者周期性的执行我们指定的方法的对象.可以按照一定的时间间隔,将制定的信息发送给目标对象.并更新某个对象的行为.你可以选择在未来 ...

随机推荐

  1. damon

    不管是否有-f参数,最终程序都会进入 fuse_loop_mt 循环中,在helper.c的fuse_main_common函数中. 1. 有-f参数.这种情况下fuse_setup_common函数 ...

  2. Python机器视觉编程常用数据结构与示例

    本文总结了使用Python进行机器视觉(图像处理)编程时常用的数据结构,主要包括以下内容: 数据结构 通用序列操作:索引(indexing).分片(slicing).加(adding).乘(multi ...

  3. 「网络流24题」「LuoguP2774」方格取数问题(最大流 最小割

    Description 在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法.对于给定的方 ...

  4. Python的单元测试工具——doctest

    doctest是一个python标准库自带的轻量单元测试工具,适合实现一些简单的单元测试.它可以在docstring中寻找测试用例并执行,比较输出结果与期望值是否符合. 基本用法使用doctest需要 ...

  5. bzoj1089严格n元树——DP+高精度

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1089 f[d]为深度小于等于d的树的个数: 从根节点出发,有n个子树,乘法原理可以得到 f[ ...

  6. 从MyEclipse到IntelliJ IDEA

    如何做到全键盘操作呢? 1.自定义快捷键实现全屏操作 你可以设置自定义快捷键进入全屏操作,并实现各个窗口之间的切换.这样,你就可以告别小窗口的时代,体验全屏显示的效果了!(相信有过多年开发经验的你一定 ...

  7. bzoj2144

    二分+lca 我们把向中间缩看成向上爬,向两边走看成向下爬,那么就相当于找出两个状态的lca,如果相邻的差是(a,b),a<b,那么向中间走就是(a,b-a)或(b-a,a),这个东西很像更相减 ...

  8. thiis also a test

    EL表达式 1.EL简介 1)语法结构 ${expression} 2)[]与.运算符 EL 提供.和[]两种运算符来存取数据. 当要存取的属性名称中包含一些特殊字符,如.或?等并非字母或数字的符号, ...

  9. 线程间操作无效: 从不是创建控件“xxxxxxxx”的线程访问它。

    方法一: Control.CheckForIllegalCrossThreadCalls = false; 方法二:(推荐) this.Invoke(new MethodInvoker(() => ...

  10. LXD安装

    #安装 #初始化(一路next) sudo lxd init #启动容器 lxc launch ubuntu:16.04 first #进到容器内 lxc exec first -- /bin/bas ...