在非主线程里面使用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文件:
#import "Mythread.h"
@implementation Mythread
- (void)dealloc
{
NSLog(@"Thread:%p dealloc",self);
}
+ (void)exit
{
NSLog(@"Thread:%p exit",self);
// 注意这是个类函数
[super exit];
}
@end
- 创建NSThread和NSTimer对象
@property (nonatomic , strong) NSThread *timerThread; @property (nonatomic , strong) NSTimer *timer;
- 定义设置NSTimer的函数
- (void)createTimer
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFire) userInfo:nil repeats:YES];
// 创建的线程中,runloop是不会自己启动的,需要手动启动
[[NSRunLoop currentRunLoop] run];
NSLog(@"createTimer,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
}
- 定义self.timer的定时任务函数
- (void)timerFire
{
static NSInteger counter = ;
NSLog(@"%@,main:%@,counter:%@",[NSThread currentThread],@([NSThread isMainThread]),@(counter++));
}
- 定义销毁self.timer的函数
- (void)destoryTimerAndThread
{
NSLog(@"destoryTimerAndThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
[self.timer invalidate];
@autoreleasepool {
self.timerThread = nil;
self.timer = nil;
}
// 释放打开的资源和清空申请的内存后,才可以退出,不然就会内存泄露
[Mythread exit];
}
- 定义启动新线程的函数
- (void)createAndStartThread
{
NSLog(@"createAndStartThread,currentThread:%@,isMainThread:%@",[NSThread currentThread],@([NSThread isMainThread]));
self.timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(createTimer) object:nil];
[self.timerThread start];
}
- 定义销毁新线程的函数
- (void)destoryThread
{
[self performSelector:@selector(destoryTimer) onThread:self.timerThread withObject:nil waitUntilDone:NO];
}
调用过程
- 主线程中调用createAndStartThread启动线程和NSTimer
- 隔一小会,主线程中调用destoryThread销毁NSTimer和线程
- 隔一小会,主线程中调用createAndStartThread启动
- 隔一小会,主线程中调用destoryThread销毁NSTimer和线程
console输出结果
::07.166 : createAndStartThread,currentThread:<NSThread: 0x127d0b750>{number = , name = main},isMainThread:
::08.171 : timerFire,currentThread:<Mythread: 0x127d05810>{number = , name = (null)},isMainThread:,counter:
::09.173 : timerFire,currentThread:<Mythread: 0x127d05810>{number = , name = (null)},isMainThread:,counter:
::10.174 : timerFire,currentThread:<Mythread: 0x127d05810>{number = , name = (null)},isMainThread:,counter:
::11.174 : timerFire,currentThread:<Mythread: 0x127d05810>{number = , name = (null)},isMainThread:,counter:
::11.479 : destoryTimerAndThread,currentThread:<Mythread: 0x127d05810>{number = , name = (null)},isMainThread:
::11.481 : Thread:0x100011158 exit
::11.482 : Thread:0x127d05810 dealloc
::16.113 : createAndStartThread,currentThread:<NSThread: 0x127d0b750>{number = , name = main},isMainThread:
::17.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = , name = (null)},isMainThread:,counter:
::18.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = , name = (null)},isMainThread:,counter:
::19.124 : timerFire,currentThread:<Mythread: 0x127d21700>{number = , name = (null)},isMainThread:,counter:
::20.126 : timerFire,currentThread:<Mythread: 0x127d21700>{number = , name = (null)},isMainThread:,counter:
::21.126 : timerFire,currentThread:<Mythread: 0x127d21700>{number = , name = (null)},isMainThread:,counter:
::21.382 : destoryTimerAndThread,currentThread:<Mythread: 0x127d21700>{number = , name = (null)},isMainThread:
::21.383 : Thread:0x100011158 exit
::21.385 : Thread:0x127d21700 dealloc
示例说明
- 为了节省显示空间,删除了部分 log 头信息
- 创建和销毁时不一定要在主线程里面调用,只是为了方便比对输出结果
- 在销毁 Timer 时,也不一定就要销毁线程,这里只是演示非主线程的创建和销毁
在非主线程里面使用NSTimer创建和取消定时任务的更多相关文章
- ThreadLocal ——android消息机制handler在非主线程创建not called Looper.prepare() 错误的原因
引用自:https://www.jianshu.com/p/a8fa72e708d3 引出: 使用Handler的时候,其必须要跟一个Looper绑定.在UI线程可直接初始化Handler来使用.但是 ...
- 在非主线程中更新UI
在非主线程中调用了showMessage方法,结果报错:Can't create handler inside thread that has not called Looper.prepare() ...
- Android 在非主线程无法操作UI意识
Android在应用显示Dialog是一个非常easy事儿,但我从来没有尝试过Service里面展示Dialog. 经验UI操作要在主线程,本地的服务Service是主线程里没错,可是远程servic ...
- 后台子线程(非主线程)更新UI引起的警告
一.问题描述 -(void)sendAsynchronousRequest { NSLog(@"%@",[NSThread currentThread]); [SVProgress ...
- Handler详解系列(四)——利用Handler在主线程与子线程之间互发消息,handler详解
MainActivity如下: package cc.c; import android.app.Activity; import android.os.Bundle; import android. ...
- Android关于主线程和非主线程
必须在主线程执行的任务: (1)UI更新 必须在非主线程中执行的任务 (1)Http请求 如执行:ImageHelper.getInstance().loadImageSync(picUrl); 外面 ...
- (原)Android在子线程用handler发送的消息,主线程是怎么loop到的?
来自知乎:https://www.zhihu.com/question/48130951?sort=created 大家都知道Android的Looper是ThreadLocal方式实现,每个线程 ...
- UNIX环境高级编程——主线程与子线程的退出关系
我们在一个线程中经常会创建另外的新线程,如果主线程退出,会不会影响它所创建的新线程呢?下面就来讨论一下. 1. 主线程等待新线程先结束退出,主线程后退出.正常执行. 示例代码: #include & ...
- Android 快速切换到主线程更新UI的几种方法
此最近看了网上,在子线程更新UI的方法,说法很多,但都不是很全面.在此我争取做到总结的全面一些,希望以后对自己,对大家都有一些帮助. 方法一: view.post(Runnable action) 假 ...
随机推荐
- Ubuntu系统下环境安装遇到依赖冲突问题
问题场景:在ubuntu系统下使用docker拉了一个python3.6的镜像,要在该容器中安装vim结果总是报已安装某些依赖的版本不满足要求 解决方法: 1.安装aptitude apt-get i ...
- 研究开源源码之Myrmec
好久没写博客了,自己也弄不清是懒了还是忙了.毕竟白天需要工作,晚上有时候看看资料,有时候陪家人,有时候约朋友......更加累了,可能由于累了就懒得总结了. 今天有同事问我关于代码检查文件类型的问题. ...
- Python入门的三大问题和三大谎言
Python广告,铺天盖地,小白们雾里看花,Python无限美好.作为会20几种语言(BASIC Foxbase/pro VB VC C C++ c# js typescript HTML Ardui ...
- iOS中使用block进行网络请求回调
iOS中使用block进行网络请求回调 HttpRequest.h // // HttpRequest.h // UseBlockCallBack // // Created by Michael o ...
- 2019-2020-2 20175226 王鹏雲 网络对抗技术 Exp2 后门原理与实践
2019-2020-2 20175226 王鹏雲 网络对抗技术 Exp2 后门原理与实践 实验内容 使用netcat获取主机操作Shell,cron启动: 使用socat获取主机操作Shell, 任务 ...
- tomcat 对 vue的history默认支持 tomcat 开启步骤 1.build文件放入webapps目录 2.进入conf目录修改server.xml端口号改成8088 3.进入bin目录运行startup.bat 4.浏览器 localhost:8088/workName 访问即可
tomcat 对 vue的history默认支持 tomcat 开启步骤 1.build文件放入webapps目录 2.进入conf目录修改server.xml端口号改成8088 3.进入bin目录运 ...
- Contest 158
2019-10-14 15:30:38 总体感受:这次依然很快搞定了前三题,最后一题乍看之下还是比较简单的,但是出奇多的corner case让我非常苦恼,这也让我意识到要想真正征服最后一题,还有一个 ...
- [gcd,灵感] Codeforces 1200C Round Corridor
题目:https://codeforces.com/contest/1200/problem/C C. Round Corridor time limit per test 1 second memo ...
- Java基础语法(8)-数组中的常见排序算法
title: Java基础语法(8)-数组中的常见排序算法 blog: CSDN data: Java学习路线及视频 1.基本概念 排序: 是计算机程序设计中的一项重要操作,其功能是指一个数据元素集合 ...
- 一书吃透机器学习!新版《机器学习基础》来了,教材PDF、PPT可下载 | 资源
不出家门,也能学习到国外高校的研究生机器学习课程了. 今天,一本名为Foundations of Machine Learning(<机器学习基础>)的课在Reddit上热度飙升至300, ...