• 在非主线程里面使用NSTimer创建和取消定时任务


    为什么要在非主线程创建NSTimer

    • 将 timer 添加到主线程的Runloop里面本身会增加线程负荷
    • 如果主线程因为某些原因阻塞卡顿了,timer 定时任务触发的时间精度肯定也会受到影响
    • 有些定时任务不是UI相关的,本来就没必要在主线程执行,给主线程增加不必要的负担。当然也可以在定时任务执行时,手动将任务指派到非主线程上,但这也是有额外开销的。

     

    NSTimer的重要特性

    • NSTimer上的定时任务是在创建NSTimer的线程上执行的。NSTimer的销毁和创建必须在同一个线程上操作
    • NSTimer要被添加到当前线程的 Runloop 里面且 Runloop 被启动,定时任务(selector或者invocation)才会触发。

     

    如何创建NSTimer对象

    多数情况下,如此一行代码创建的NSTimer就能正常工作:

    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire) userInfo:nil repeats:YES]

    因为这段创建代码是在主线程里面执行的,主线程里面会有系统创建好了的且已经启动了的 Runloop :[NSRunLoop mainRunLoop]。通过[NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:]创建时,会自动将创建的NSTimer对象加到当前的 Runloop 里面,所以 timer 能够创建后立马就能工作。

     

    根据以上,可以这么创建自定义线程和运行在上面的 timer :

    创建线程对象和NSTimer对象,定义函数

    • 子类化NSThreadMythread,重载了deallocexit函数,在里面加了 log 输出,方便跟踪执行过程
      • Mythread.h文件为默认,略去
      • Mythread.m文件:
     1 #import "Mythread.h"
     2 @implementation Mythread
     3 - (void)dealloc
     4 {
     5     NSLog(@"Thread:%p dealloc",self);
     6 }
     7 + (void)exit
     8 {
     9     NSLog(@"Thread:%p exit",self);
    10     // 注意这是个类函数
    11     [super exit];
    12 }
    13 @end
    • 创建NSThreadNSTimer对象
    1 @property (nonatomic , strong) NSThread *timerThread;
    2 
    3 @property (nonatomic , strong) NSTimer *timer;
    • 定义设置NSTimer的函数
     1 - (void)createTimer
     2 
     3 {
     4 
     5     self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire) userInfo:nil repeats:YES];
     6 
     7     // 创建的线程中,runloop是不会自己启动的,需要手动启动
     8 
     9     [[NSRunLoop currentRunLoop] run];
    10 
    11     NSLog(@"createTimer,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
    12 
    13 }
    • 定义self.timer的定时任务函数
    1 - (void)timerFire
    2 
    3 {
    4 
    5     static NSInteger counter = 0;
    6 
    7     NSLog(@"%@,main:%@,counter:%@",[NSThread currentThread],@([NSThread isMainThread]),@(counter++));
    8 
    9 }  
    • 定义销毁self.timer的函数
     1 - (void)destoryTimerAndThread
     2 
     3 {
     4 
     5     NSLog(@"destoryTimerAndThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
     6 
     7     [self.timer invalidate];
     8 
     9     @autoreleasepool {
    10 
    11         self.timerThread = nil;
    12 
    13         self.timer = nil;
    14 
    15     }
    16 
    17     // 释放打开的资源和清空申请的内存后,才可以退出,不然就会内存泄露
    18 
    19     [Mythread exit];
    20 
    21 }
    • 定义启动新线程的函数
     1 - (void)createAndStartThread
     2 
     3 {
     4 
     5     NSLog(@"createAndStartThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
     6 
     7     self.timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(createTimer) object:nil];
     8 
     9     [self.timerThread start];
    10 
    11 }
    • 定义销毁新线程的函数
    1 - (void)destoryThread
    2 
    3 {
    4 
    5     [self performSelector:@selector(destoryTimer) onThread:self.timerThread withObject:nil waitUntilDone:NO];
    6 
    7 }

    调用过程

    1. 主线程中调用createAndStartThread启动线程和NSTimer
    2. 隔一小会,主线程中调用destoryThread销毁NSTimer和线程
    3. 隔一小会,主线程中调用createAndStartThread启动
    4. 隔一小会,主线程中调用destoryThread销毁NSTimer和线程

    console输出结果

     1 16:16:07.166 : createAndStartThread,currentThread:<NSThread: 0x127d0b750>{number = 1, name = main},isMainThread:1
     2 
     3 16:16:08.171 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:0
     4 
     5 16:16:09.173 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:1
     6 
     7 16:16:10.174 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:2
     8 
     9 16:16:11.174 : timerFire,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0,counter:3
    10 
    11 16:16:11.479 : destoryTimerAndThread,currentThread:<Mythread: 0x127d05810>{number = 2, name = (null)},isMainThread:0
    12 
    13 16:16:11.481 : Thread:0x100011158 exit
    14 
    15 16:16:11.482 : Thread:0x127d05810 dealloc
    16 
    17 16:16:16.113 : createAndStartThread,currentThread:<NSThread: 0x127d0b750>{number = 1, name = main},isMainThread:1
    18 
    19 16:16:17.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:4
    20 
    21 16:16:18.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:5
    22 
    23 16:16:19.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:6
    24 
    25 16:16:20.126 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:7
    26 
    27 16:16:21.126 : timerFire,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0,counter:8
    28 
    29 16:16:21.382 : destoryTimerAndThread,currentThread:<Mythread: 0x127d21700>{number = 3, name = (null)},isMainThread:0
    30 
    31 16:16:21.383 : Thread:0x100011158 exit
    32 
    33 16:16:21.385 : Thread:0x127d21700 dealloc

    示例说明

      • 为了节省显示空间,删除了部分 log 头信息
      • 创建和销毁时不一定要在主线程里面调用,只是为了方便比对输出结果
      • 在销毁 Timer 时,也不一定就要销毁线程,这里只是演示非主线程的创建和销毁
  • 相关阅读:
    ie下如果已经有缓存,load方法的效果就无法执行.的解决方法
    css公共样式
    pageX、pageY全兼容
    js滚动加载插件
    getComputedStyle()与currentStyle
    excel15个技巧
    XMLHttpRequest函数
    继承模式
    cookie函数
    jQuery添加删除元素
  • 原文地址:https://www.cnblogs.com/boch2436/p/5836417.html
Copyright © 2020-2023  润新知