【iOS】—— RunLoop线程常驻和线程保活
文章目录
- 没有线程常驻会怎么样?
- 线程常驻
- 线程保活
没有线程常驻会怎么样?
我们一般写一个子线程,子线程执行完分配的任务后就会自动销毁,比如下面这个情况:
我们先重写一下NSThread里面的dealloc方法,打印什么时候会调用dealloc方法。
#import "NSThread+NewDealloc.h"@implementation NSThread (NewDealloc)
- (void)dealloc {NSLog(@"%s", __func__);
}
@end
在ViewController里调用方法:
@implementation FirstViewController- (void)viewDidLoad {[super viewDidLoad];NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil];[thread start];
}
- (void)doSomeThing {NSLog(@"%s", __func__);
}

根据打印结果我们可以看到,在子线程执行完任务后线程自动销毁。
而我们有时会需要经常在一个子线程中执行任务,频繁的创建和销毁线程就会造成资源浪费,这时候就要用到RunLoop来使线程长时间存活了
线程常驻
开发应用程序的过程中,如果后台操作十分频繁,比如后台播放音乐、下载文件等等,我们希望这条线程永远常驻内存
我们可以添加一条用于常驻内存的强引用子线程,在该线程的RunLoop下添加一个Sources,开启RunLoop
@interface FirstViewController ()
@property (nonatomic, strong) NSThread *thread;
@end
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(run1) object:nil];[self.thread start];
- (void)run1 {NSLog(@"----run1-----");/*如果不加这句,会发现runloop创建出来就挂了,因为runloop如果没有CFRunLoopSourceRef事件源输入或者定时器,就会立马消亡。下面的方法给runloop添加一个NSport,就是添加一个事件源,也可以添加一个定时器,或者observer,让runloop不会挂掉*/[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];// 方法1 ,2,3实现的效果相同,让runloop无限期运行下去// 方法2
// [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];// 方法3
// [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];[[NSRunLoop currentRunLoop] run];// 测试是否开启了RunLoop,如果开启RunLoop,则来不了这里,因为RunLoop开启了循环。NSLog(@"未开启RunLoop");
}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {// 利用performSelector,在self.thread的线程中调用run2方法执行任务[self performSelector:@selector(run2) onThread:self.thread withObject:nil waitUntilDone:NO];
}- (void)run2 {NSLog(@"----run2------");
}

无论点击屏幕多少次都不会dealloc线程。
我们必须保证线程不消亡,才可以在后台接受时间处理,所以如果没有实现添加NSPort或者NSTimer,会发现执行完run方法,线程就会消亡,后续再执行touchbegan方法无效。
实现了上面三个方法之一,就可以发现执行完了run方法,这个时候再点击屏幕,可以不断执行test方法,因为线程self.thread一直常驻后台,等待事件加入其中,然后执行。
线程保活
RunLoop的启动和关闭方法在上一篇博客的最后讲过【iOS】—— RunLoop初学
我们直奔主题:
通过以上关于RunLoop启动和关闭的方法分析,我们大概有这样一个思路:
- 我们想要控制RunLoop,就需要使用runMode:beforeDate:方法,因为其他两种方法一个无法停止一个只能依赖超时机制
- CFRunLoopStop() 方法只会结束当前的一次的runMode:beforeDate:方法调用,我们必须再做点什么
针对以上疑问,有以下解答:
- 首先,因为runMode:beforeDate:方法是单次调用,我们需要给它加上一个循环,否则调用一次就over了,和不使用RunLoop的效果大同小异
- 这个循环的条件可以默认设置为YES,当调用stop方法时,执行CFRunLoopStop() 方法并且将循环条件改为NO,就可以使循环停止,RunLoop退出
#import "SecondViewController.h"@interface SecondViewController ()
@property (nonatomic, strong) NSThread *aThread;
@property (nonatomic, assign) BOOL stopped;
@end@implementation SecondViewController- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor whiteColor];// 添加一个停止RunLoop的按钮UIButton *stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];[self.view addSubview:stopButton];stopButton.frame = CGRectMake(180, 180, 100, 50);stopButton.titleLabel.font = [UIFont systemFontOfSize:20];[stopButton setTitle:@"stop" forState:UIControlStateNormal];stopButton.tintColor = [UIColor blueColor];[stopButton addTarget:self action:@selector(stop) forControlEvents:UIControlEventTouchUpInside];// 用于返回的按钮UIButton *backButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];[self.view addSubview:backButton];backButton.frame = CGRectMake(180, 380, 100, 50);backButton.titleLabel.font = [UIFont systemFontOfSize:20];[backButton setTitle:@"back" forState:UIControlStateNormal];backButton.tintColor = [UIColor orangeColor];[backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];self.stopped = NO;__weak typeof(self) weakSelf = self;self.aThread = [[NSThread alloc] initWithBlock:^{NSLog(@"go");[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];while (!weakSelf.stopped) {[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];}NSLog(@"ok");}];[self.aThread start];
}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[self performSelector:@selector(doSomething) onThread:self.aThread withObject:nil waitUntilDone:NO];
}// 子线程需要执行的任务
- (void)doSomething {NSLog(@"%s %@", __func__, [NSThread currentThread]);
}- (void)stop {// 在子线程调用stopif (self.aThread) {// 在子线程调用stop[self performSelector:@selector(stopThread) onThread:self.aThread withObject:nil waitUntilDone:YES];}
}// 用于停止子线程的RunLoop
- (void)stopThread {// 设置标记为NOself.stopped = YES;// 停止RunLoopCFRunLoopStop(CFRunLoopGetCurrent());NSLog(@"%s %@", __func__, [NSThread currentThread]);self.aThread = nil;
}- (void)dealloc {NSLog(@"%s", __func__);
}- (void)back {[self stop];[self dismissViewControllerAnimated:YES completion:nil];
}
@end
需要注意的一点是,如果我们的ViewController已经被销毁了,线程并没有死,这就造成了内存泄漏了。所以我们要注意,在ViewController被dealloc之前,先stop线程。

相关文章:
【iOS】—— RunLoop线程常驻和线程保活
文章目录 没有线程常驻会怎么样? 线程常驻线程保活 没有线程常驻会怎么样? 我们一般写一个子线程,子线程执行完分配的任务后就会自动销毁,比如下面这个情况: 我们先重写一下NSThread里面的dealloc方法,打印…...
Springcloud--docker快速入门
认识docker docker相关操作 1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中,依赖的组件非常多,不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署…...
基于AT89C51单片机的电子计数器设计与仿真
点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/87770826 源码获取 主要内容: 设计一个电子计时器,数码管初始显示值为“00”,每隔1s电子秒表加1;秒计数到60时清0&a…...
IT程序员如何面对35岁大龄问题?我从公司老板的角度聊聊
很多从事IT行业的人一想到35岁就很焦虑,担心自己被公司裁员后找不到工作。同时还有家庭责任加身,担心中年失业后晚年生活。作为一位公司老板,我想从我的角度谈一下这个问题。 首先,我本质上不介意我的员工年龄,无论是…...
【计算机专业漫谈】【计算机系统基础学习笔记】W2-2-2 模运算系统和补码表示
利用空档期时间学习一下计算机系统基础,以前对这些知识只停留在应试层面,今天终于能详细理解一下了。参考课程为南京大学袁春风老师的计算机系统基础MOOC,参考书籍也是袁老师的教材,这是我的听课自查资料整理后的笔记 补码表示法…...
vue概述
vue2和vue3的区别 vue2和vue3区别 NOvue2vue31 optinos Api写法 比较分散 Compostiton Api 代码集 2重写数序双向绑定通过Object.defineProperty()实现 基于Proxy实现 对数组有了更好的支持 3Fragments 1,在template中只能一个div 2…...
SpringCloud-OpenFeign案例实战
关于Spring Cloud Open Feign的介绍可以参考这两篇博客 OpenFeign服务接口调用 使用Feign作为服务消费者 本博客参考gitee开源项目代码,结合自己的理解,记录下微服务场景下的使用。Talk is cheap. Show me the code! 一、项目结构 这里使用…...
ACM - 数学 - 提高(还没学多少)
ACM - 数学 练习题 一、数论1、分解质因数 :AcWing 197. 阶乘分解2、求约数个数(1)AcWing 1294. 樱花 (求 n!约数个数之和)(2)AcWing 198. 反素数 (求 1 ~ N 中约数最多的…...
JavaScript class和继承的原理
(对于不屈不挠的人来说,没有失败这回事。——俾斯麦) class 相关链接 MDN链接 有关类的详细描述 关于构造函数,原型和原型链的说明 类的概述 类是用于创建对象的模板。他们用代码封装数据以处理该数据。JS 中的类建立在原型上…...
Playwright-python 自动化测试【Anaconda】环境配置
第一步:Anaconda的安装 安装Anaconda的好处,比prenv网速快,并且拥有独立的python环境,再也不用烦恼用哪个python好了。 Anaconda的下载页参见官网下载,Linux、Mac、Windows均支持。 https://mirrors.tuna.tsinghua.ed…...
攻防世界-web-simple js
题目描述:小宁发现了一个网页,但却一直输不对密码。(Flag格式为 Cyberpeace{xxxxxxxxx} ) 打开链接: 然后我们会发现不管我们输入什么密码,发现是都是这样的报错 1. 先用bp抓包看看,可以抓到这样的一串js脚本 看不懂…...
【SpringCloud】初始微服务
目录 一、单体架构 1、概念 2、优点 3、缺点 二、分布式架构 1、概念 2、优点 3、缺点 三、微服务 1、概念 2、优点 3、缺点 四、微服务技术对比 五、SpringCloud 六、服务拆分 1、注意事项 2、服务远程调用 一、单体架构 1、概念 业务的所有功能都集中到一个…...
均摊时间复杂度
均摊时间复杂度,它对应的分析方法,摊还分析(或者叫平摊分析) 均摊时间复杂度应用的场景比它更加特殊、更加有限 // array表示一个长度为n的数组// 代码中的array.length就等于nint[] array new int[n];int count 0;void insert…...
夏驰和徐策的解决数学问题思路——反证法
反证法是一种证明方法,它的基本思路是通过假设某个结论不成立,然后构造出一个矛盾的情况来推导出原先假设的结论是成立的。 具体来说,反证法一般包含以下步骤: 1. 假设所要证明的命题不成立。 2. 通过这个假设,构造…...
面向开发人员的 ChatGPT 提示词教程 - ChatGPT Prompt Engineering for Developers
面向开发人员的 ChatGPT 提示词教程 - ChatGPT Prompt Engineering for Developers 1. 指南(原文: Guidelines)1-1. 提示的指南(原文: Guidelines for Prompting)1-2. 配置1-3. 提示语原则(原文: Prompting Principles)原则 1: 写出清晰而具体的指示(原文: Write clear and spe…...
虹科方案|使用 HK-TRUENAS支持媒体和娱乐工作流程-1
一、摘要 开发和交付能够随时随地触及受众的媒体内容变得越来越重要和复杂。 在当今高度互联、娱乐驱动的世界中,媒体和娱乐 (M&E) 公司需要保持竞争力才能取得成功。 这些组织需要制作各种不同格式的信息和娱乐内容,以便在移动设备、台式机、工作站…...
DDR5内存彻底白菜价,国外大厂却整出了比着火更离谱的骚操作
今年的 PC 硬件市场,似乎出现了明显两极分化现象。 一边是 N、A 两家新显卡价格高高在上,摆明了不坑穷人。 另一边固态硬盘、内存条又在疯狂互卷不断杀价。 四五百元的 2TB SSD,二百元的 16G 内存条早已见怪不怪。 要说面世多年的 PCIe 3.0…...
Linux网络——Shell编程之函数
Linux网络——Shell编程之函数 一、概述二、定义函数的格式1.格式一2.格式二 三、函数的查看和删除1.查看 declare2.删除 declare 四、函数的返回值1.return 返回值2.echo 返回值 五、函数的参数传入与变量范围1.函数的传参2.函数变量的作用范围 六、函数的应用1.阶乘2.递归目录…...
GQCNN+PointNetGPD思路和问题--chatGPT
有很多算法是通过神经网络来预测机械臂抓手的抓取位置,其中一些算法需要点云数据作为输入,例如: PointNetGPD:PointNetGPD是一个端到端的基于点云的抓取姿态检测算法。它使用了一个PointNet架构来处理点云输入,并输出每…...
Mysql索引(2):索引结构
1 概述 MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的索引结构,主要包含以下几种: 索引结构描述BTree索最常见的索引类型,大部分引擎都支持 B 树索引 Hash索引 底层数据结构是用哈希表实现的, 只有精确匹配索引列的…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
