自欺欺人的使用 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. codeforces 459 B.Pashmak and Flowers 解题报告

    题目链接:http://codeforces.com/problemset/problem/459/B 题目意思:有 n 朵 flowers,每朵flower有相应的 beauty,求出最大的beau ...

  2. 1Q84 BOOK 1-3 笔记

    1Q84 BOOK 1-3 村上春树 18个笔记     ◆ 第2章 青豆 不要被外表迷惑   >> 在这三十年的人生中,究竟听过多少次同样的台词?为了这个姓氏,曾经多少次遭人调侃?如果不 ...

  3. GCD基础知识

    并行和并发 在英文世界里,「并行」和「并发」的区别比较清晰,「并行」对应parallelism,「并发」对应concurrency:但在中文世界里二者仅一字之差,两个概念非常容易弄混淆: 各种资料对「 ...

  4. iOS成员变量、实例变量、属性变量三者的联系与区别

    一.类Class中的属性property 在ios第一版中: 我们为输出口同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如: 注意:(这个是 ...

  5. Watir: 如何处理简单的网页弹出警告框?

    以下是一个很经典的把Watir与AutoIt连接在一起的实例.如果我们对AutoIT了解的更多,处理类似的问题会更加简单.以下实例会判断页面上是否有某“删除”链接,一旦有该链接,就点击,然后点击弹出的 ...

  6. ASP.NET Core:WebAppCoreRESTful

    ylbtech-ASP.NET Core:WebAppCoreRESTFul 1.返回顶部 1. 2. 3.         4. 2. Controllers返回顶部 1.HomeControlle ...

  7. UI:UICollectionView

    #import "ViewController.h" #import "HeaderView.h" #import "FooterView.h&quo ...

  8. Linux常用的18个命令(复习)

    1. cd命令 它用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径.如: cd /root/Docements # 切换到目录/root/Docements cd . ...

  9. [msf]CentOS VPS创建pptpd 并搭建msf

    安装pptpd服务 vps下 下载 centos 6 一键安装包 wget --no-check-certificate https://raw.githubusercontent.com/teddy ...

  10. PAT 1035 插入与归并(25)

    原题:https://pintia.cn/problem-sets/994805260223102976/problems/994805286714327040传送门: 根据维基百科的定义: 插入排序 ...