美文网首页iOS开发技术IOSiOS进阶指南
iOS 在子线程中NSTimer的启动和关闭

iOS 在子线程中NSTimer的启动和关闭

作者: Cheney_Chen | 来源:发表于2016-03-16 15:08 被阅读2364次

    之前在项目中遇见了一个问题,在子线程中如何开启NSTimer和取消NSTimer。现在总结一下,当做自己的笔记。

    1.子线程中NSTimer的开启

    首先在.m文件中声明两个属性一个是子线程 一个是子线程中的NSTimer。

    
    @property (nonatomic, strong) NSThread *thread1;
    
    @property (nonatomic, strong) NSTimer *threadTimer;
    
    

    然后用GCD在全局全队中创建一个子线程并创建NSTimer。

        __weak __typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            __strong __typeof(weakSelf) strongSelf = weakSelf;
            if (strongSelf) {
                strongSelf.thread1 = [NSThread currentThread];
                [strongSelf.thread1 setName:@"线程A"];
                strongSelf.threadTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:strongSelf selector:@selector(timerAction) userInfo:nil repeats:YES];
                NSRunLoop *runloop = [NSRunLoop currentRunLoop];
                [runloop addTimer:strongSelf.threadTimer forMode:NSDefaultRunLoopMode];
                [runloop run];
            }
        });
    

    注意的是:在子线程中创建的NSTimer需要加入到对应线程的RunLoop中。RunLoop中常用的mode有:NSDefaultRunLoopMode、UITrackingRunLoopMode和NSRunLoopCommonModes三种模式。

    NSDefaultRunLoop 默认模式

    UITrackingRunLoop 界面追踪,用于scrollView拖拽 滑动

    NSRunLoopCommonModes 不是一个特定的模式,只是一种标记,比较综合的一种模式(包含 前两种模式)

    在NSTimer加入RunLoop后,需要将RunLoop运行起来。

    2.子线程中NSTimer的关闭

    之后创建一个cancelTimer的方法

    - (void)cancel{
    
    if (self.threadTimer) {
    
    [self.threadTimer invalidate];
    
    self.threadTimer = nil;
    
    }
    
    }
    

    如果这个方法跟创建NSTimer不在同一个线程执行是无法将Timer 执行invalidate操作的。

    然后现在我们需要在thread1这个线程中执行这个操作,在这里写一个方法用于在子线程中调用此方法。

    - (void)cancelTimer{
    
    if (self.threadTimer && self.thread1) {
    
    [self performSelector:@selector(cancel) onThread:self.thread1 withObject:nil waitUntilDone:YES];
    
    }
    
    }
    

    最后我们在有需要关闭Timer的地方执行此方法即可。

    在这里说明一下为什么NSTimer要在同一个线程中创建和关闭。因为创建的Timer的时候已经把Timer加入到该线程对应的RunLoop中,这个RunLoop设置了这个Timer为一个事件。因此要在同一个线程中才能cancel这个Timer。

    相关文章

      网友评论

      • 一个帅蛋:timer创建的代码有问题,timer根本没有开到thread1中。
        一个帅蛋:应该是- (void)viewDidLoad
        {
        [super viewDidLoad];

        NSLog(@"主线程 %@", [NSThread currentThread]);

        //创建并执行新的线程
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
        [thread start];
        }

        - (void)newThread
        {
        @autoreleasepool
        {
        //在当前Run Loop中添加timer,模式是默认的NSDefaultRunLoopMode
        [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
        //开始执行新线程的Run Loop
        [[NSRunLoop currentRunLoop] run];
        }
        }
      • unhangcorn:博主,有个问题,我跑了一下上面的代码,发现
        即使是使用- (void)cancel; 也能停止timer的执行.timer也是有变成nil的,这是为什么呢?
      • unhangcorn:感谢分享,好人一生平安❤️
      • 穷寇:兄弟,你的思路没问题。但是有一点你没注意,子线程的runLoop在你初始化完定时器被你启动了,虽然最后从相应的子线程的runLoop移除了定时器,但是此时子线程runLoop还在,导致控制器不能被释放,你会发现你控制器的dealloc根本不走。
        穷寇:@doudo 测试发现什么新问题,欢迎指正
        穷寇:@doudo ARC下的关键代码:

        @property (nonatomic,strong) NSTimer *timer;
        @property (nonatomic,strong) NSThread* timerThread;

        - (void)viewDidLoad
        {
        [super viewDidLoad];

        _timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(creatTimer) object:nil];
        [_timerThread start];

        //监听子线程的释放(为了证明下面@autoreleasepool{}的写法可以释放我们的子线程)
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadfinishTask:) name:NSThreadWillExitNotification object:nil];
        }
        - (void)threadfinishTask:(NSNotification *)notification{

        NSLog(@"==============================子线程被释放了!");
        }

        -(void)creatTimer
        {
        @autoreleasepool//释放子线程
        {
        _timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(dylog) userInfo:@{@"key":@"value"} repeats:true];
        do
        {
        //效果:可以实时监测到_timer的释放,从而实时的释放线程,最终控制器也可以被释放
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        } while (_timer);
        }
        }



        -(void)timerInvalidate
        {
        if (_timer)
        {
        if ([_timer isValid])//如果timer还在runLoop中
        {
        [_timer invalidate];
        NSLog(@"查看invalidate的执行是否和timer的创建在同一个线程!");
        }
        _timer = nil;
        }
        }

        #pragma mark - NSTimer在子线程的释放测试

        -(void)viewWillDisappear:(BOOL)animated
        {
        [super viewWillDisappear:animated];

        [self performSelector:@selector(timerInvalidate) onThread:_timerThread withObject:nil waitUntilDone:YES];
        }

        -(void)dealloc
        {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        NSLog(@"MultiThreadUsingVC -- dealloc!");
        }
        doudo:正解,我也发现了,求正解啊兄弟。
      • 温柔vs先生:cancelTimer 这个方法能放在dealloc中吗
      • liziNo1:科学研究表明,在主线程中是可以关闭的啊,你这个是不是有问题?
        Cheney_Chen:@liziNo1 官方文档建议 在同一线程开关timer
      • 给我一支烟smoking:请问你这句话[runloop run];开启子线程的runloop 会影响到这个线程exit 吗?thread1这个线程什么时候exit ;
      • Nihility_Ming:这个线程已经结束了?
        Cheney_Chen:@Nihility_Ming 就是我想把Timer置为nil以后就exit掉这个线程。但是不知道什么时候exit
      • Nihility_Ming:奥兹,猴塞雷。 :fearful:
        Cheney_Chen:@Nihility_Ming 会报错
        Nihility_Ming:@陈晓晨_cxc 不是在你的自定义方法- (void)cancel;里面[NSThread exit]么?
        Cheney_Chen:@Nihility_Ming 我是想问下thread1这个线程什么时候exit

      本文标题:iOS 在子线程中NSTimer的启动和关闭

      本文链接:https://www.haomeiwen.com/subject/ubiqlttx.html