自欺欺人的使用 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. 一步一步学Silverlight 2系列(16):数据与通信之JSON

    概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...

  2. 一步一步学Silverlight 2系列(11):数据绑定

    概念 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...

  3. 【Java】DateUtil(1)

    import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; impor ...

  4. Android自动化测试环境搭建

    Android自动化环境的搭建主要包括: 1. java jdk和jre的安装和环境的配置 2. appium服务器的安装和配置 3. eclipse开发工具,这里不必要用Android Studio ...

  5. 【Codeforces 664A】 Complicated GCD

    [题目链接] 点击打开链接 [算法] gcd(a,a+1) = 1 所以当a = b时,答案为a,否则为1 [代码] #include<bits/stdc++.h> using names ...

  6. javascript之this指向

    情况一: 如果一个函数中有this,但是没有被上一级调用,this指向window 例: function a(){ var num='11'; console.log(this.num);  //u ...

  7. const常量

    1.不能给常量赋值 ; a=; 编译器报错:“a”: 不能给常量赋值

  8. UVaLive 6581 && Gym 100299B What does the fox say? (模拟+STL)

    题意:给定一些动物的叫声,然后再定某些动物的叫声,让你去除这些叫声后得到的叫声. 析:先存储所有的叫声,然后用map来记录其他的叫声,在输出时再判定一下就好. 代码如下: #pragma commen ...

  9. hdoj1827

    图的强连通,缩点,求个入度为0的点的数量,和入度为0的点集里面最小的花费和. //很死板的题,模板题的一样的- #include<cstdio> #include<queue> ...

  10. .net mvc中一种简单的工作流的设计

    开篇前的废话:工作流是我们在做互联网应用开发时经常需要用到的一种技术,复杂的工作流我们基本是借助一些开源的 工作流项目来做,比如 ccflow等,但是有时候,我们只需要实现一些简单的工作流流程,这时候 ...