在非主线程里面使用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对象,定义函数

  • 子类化NSThread为Mythread,重载了dealloc和exit函数,在里面加了 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
  • 创建NSThread和NSTimer对象
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 时,也不一定就要销毁线程,这里只是演示非主线程的创建和销毁
时间: 2024-12-18 06:45:28

在非主线程里面使用NSTimer创建和取消定时任务的相关文章

多线程的非主线程的销毁机制

假设有如下代码所示的多线程: 1 private void btnNewThread_Click(object sender, EventArgs e) 2 { 3 Thread t = new Thread(new ThreadStart(Test)); 4 t.Start(); 5 this.btnNewThread.Enabled = false; 6 } 7 private void Test() 8 { 9 //-- 10 } 这个新建的线程t在执行完Test()方法后会自动销毁吗?还

Android 在非主线程无法操作UI意识

Android在应用显示Dialog是一个非常easy事儿,但我从来没有尝试过Service里面展示Dialog. 经验UI操作要在主线程,本地的服务Service是主线程里没错,可是远程service里面显示Dialog,听起来是不是就应该没有在主线程里面了呢? 尝试一下就知道了,写了个AIDL的调用,client端去调用AIDL.在Service这边就是去显示一个Dialog. AIDL的部分就忽略了.Service这边的代码就和Activity上显示dialog一样. AlertDialo

Android 关于非主线程不能操作UI的认识

Android在应用里显示Dialog是个很简单的事情,但是一直没试过在Service里面显示Dialog.根据之前的经验UI操作要在主线程,本地的服务Service是主线程里没错,但是远程service里面显示Dialog,听起来是不是就应该没有在主线程里面了呢? 尝试一下就知道了,写了个AIDL的调用,client端去调用AIDL,在Service这边就是去显示一个Dialog.AIDL的部分就忽略了.Service这边的代码就和Activity上显示dialog一样. AlertDialo

SD_WebImage-03-多线程+下载任务放入非主线程执行

1 // 2 // UIImageView+WebCache.m 3 // 02-SDWebImage 4 // 5 // Created by mac on 16/4/20. 6 // Copyright © 2016年 mac. All rights reserved. 7 // 8 9 #import "UIImageView+WebCache.h" 10 11 @implementation UIImageView (WebCache) 12 13 /** 14 * 注意点:有

非主线程创建窗口也能工作正常,只要我们注意一点:消息循环必须要和创建窗口在同一线程!

#include "stdafx.h" #include "win32.h" #include "windows.h" #include #define MAX_LOADSTRING 100 HWND g_hWnd = NULL; HINSTANCE g_hInst; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void CreateWnd(void) { WNDCLASS wc =

Android 在工作线程(非主线程)更改UI组件

Looper.prepare();          Toast.makeText(LoginActivity.this,filedId, Toast.LENGTH_LONG).show();        Looper.loop(); 这样为什么可以?然后就是loop 是个死循环,这样子真的好吗?

解决iOS程序UI主线程和定时器相互阻塞的问题

我的问题是这样,我的页面上有一个UIScrollView和一个定时器用来记录当前考试模式下的剩余时间,问题出现了:当我滑动滚动试图时,定时器的方法便不在运行(即被UI主线程阻塞).google一下找到了解决办法:将定时器放在非主线程中执行将更新UI的操作放到主线程,这样UI主线程和定时器就能互不干扰的相互工作了. 在另一个项目中,还解决了一个问题:手机验证码,获取按钮,点击获取后,会开始倒计时一段时间,按钮不可点,按钮上的文字不断变化. 获取验证码 120秒后重新获取 119秒后重新获取 以下是

【Android开发经验】来来来,同学,咱们讨论一下“只能在UI主线程更新View”这件小事

转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 "只能在UI主线程中更新View". 这句话很熟悉吧? 来来,哥们,看一下下面的例子 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) f

主线程和分线程

// //  ViewController.m //  MultiThread // //  Created by mac on 15-9-28. //  Copyright (c) 2015年 zy. All rights reserved. // #import "ViewController.h" #import "MyOpearation.h" #import "My.h" @interface ViewController () @en