iOS底层原理 - 常驻线程

在 AFN 2.0 时代,会经常看到 AFN 创建一个常驻线程的方式:

0️⃣ AFN 2.0 时代的常驻线程

+ (NSThread *)networkRequestThread {static NSThread *_networkRequestThread = nil;static dispatch_once_t oncePredicate;dispatch_once(&oncePredicate, ^{_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];[_networkRequestThread start];});return _networkRequestThread;
}+ (void)networkRequestThreadEntryPoint:(id)__unused object {@autoreleasepool {[[NSThread currentThread] setName:@"AFNetworking"];NSRunLoop *runLoop = [NSRunLoop currentRunLoop];[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];[runLoop run];}
}
复制代码

我们知道 RunLoop 是App运行的基础,并且每个线程都有其对应的唯一的 RunLoop 保证其正常运行,而且我们还知道 RunLoop 必须在特定模式下才能保持活跃状态,更多关于 RunLoop 底层的问题可参阅 iOS - Runloop 详解

1️⃣ 新时代常驻线程初试

在项目中我们也会有类似创建一个常驻线程进行某些耗时操作的需求,若每次均创建一个新的异步线程效率很低,作为优化我们需要一个常驻的异步线程。 如下代码可满足我们创建一个常驻线程的需求:

1. 声明常驻线程属性并对其强引用
```objc
@property (nonatomic, weak) NSThread *p_thread;
```
复制代码
2. 实例化常驻线程,且获取当前 RunLoop 设置一个 source 使其保活
```objc
+ (void)p_creatThreadMethod {void (^creatThreadBlock)(void) = ^ {NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];[currentRunLoop addPort:[NSPort new] forMode:NSDefaultRunLoopMode];[currentRunLoop run];};if (@available(iOS 10.0, *)) {self.p_thread = [[NSThread alloc] initWithBlock:creatThreadBlock];} else {self.p_thread = [[NSThread alloc] initWithTarget:self selector:@selector(class_creatThreadMethod:) object:creatThreadBlock];}
}+ (void)class_creatThreadMethod:(void (^)(void))block {block();
}
```
复制代码
3. 使用:在需要常驻线程执行某操作时:
```objc
- (void)threakTaskMethod:(void (^)(void))task {[self performSelector:@selector(taskMethod:) onThread:self.p_thread withObject:task waitUntilDone:NO];}
```
复制代码
弊端:此时我们创建的常驻线程无法跟随当前对象的释放而释放:因为

Apple 对 [[NSRunLoop currentRunLoop] run]; 的解释:

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers. Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit. macOS can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting. If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop. A simple example would be:

大概意思就是: 如果 RunLoop 没有定时器或输入源,这个方法会立即结束。否则他会反复调用 runMode:beforeDate: 方法,换句话说就是这个方法实际上是开启了一个无限循环用以处理 runloop 的输入源和定时器。 手动移除所有输入源和定时器并不能保证 RunLoop 会退出。MacOS 可以安装和移除额外的输入源以处理接收者线程的请求。所以这种方式可组织 RunLoop 退出。 如果你想终结 RunLoop ,你不能使用这种方式。相反,使用其他的运行方法在 RunLoop 中检测他们的状态,一个简单的?:

这时 Apple 为我们提供了一个控制 RunLoop 声明周期的例子:

BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);复制代码

所以我们可以对以上代码进行改进!

2️⃣ 控制常驻线程的生命周期

1. 声明常驻线程属性和保活状态变量
@property (nonatomic, strong) XWThread *p_thread;
@property (nonatomic, assign, getter=isShouldKeepRunning) BOOL shouldKeepRunning;
复制代码
2. 在管理类中创建常驻线程
- (NSThread *)thread {NSThread *thread = nil;__weak typeof(self) weakSelf = self;void (^creatThreadBlock)(void) = ^ {NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];[currentRunLoop addPort:[NSPort new] forMode:NSDefaultRunLoopMode];while (weakSelf && weakSelf.isShouldKeepRunning) {// runloop 只有在外部标识 shouldKeepRunning 为 YES 时才继续 run[currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];}};self.shouldKeepRunning = YES;if (@available(iOS 10.0, *)) {thread = [[NSThread alloc] initWithBlock:creatThreadBlock];} else {thread = [[NSThread alloc] initWithTarget:weakSelf selector:@selector(creatThreadMethod:) object:creatThreadBlock];}[thread start];return thread;
}- (void)creatThreadMethod:(void (^)(void))creatThreadBlock {creatThreadBlock();
}
复制代码
3. 在使用常驻线程执行某些操作时
/**在默认常驻线程中执行操作 (线程需随当前对象创建或销毁)@param task 操作*/
- (void)executeTask:(XWLivingThreadTask)task {if (!task || !self.p_thread) return;[self performSelector:@selector(threakTaskMethod:) onThread:self.p_thread withObject:task waitUntilDone:NO];
}- (void)threakTaskMethod:(void (^)(void))task {task ? task() : nil;
}
复制代码
4. 在对象销毁时手动销毁常驻线程
- (void)dealloc {[self performSelector:@selector(clearThreadMethod) onThread:self.p_thread withObject:nil waitUntilDone:YES];
}- (void)clearThreadMethod {self.shouldKeepRunning = NO; // runloop 结束标记CFRunLoopStop(CFRunLoopGetCurrent());
}
复制代码

至此,我们就封装了一个可控制其生命周期的常驻线程。美滋滋。

其中 RunLoop 保活部分代码也可使用底层 CoreFoundation 的代码实现:

    void (^creatThreadBlock)(void) = ^ {CFRunLoopSourceContext content = {0};CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &content);CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);CFRelease(source);CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);};
复制代码

3️⃣ 开源

你若问我有没有演示 Demo, 答案是有的,封装了一个小工具类用以快速创建全局常驻线程和局部常驻线程的方法。 这是代码 ↓

XWLivingThread

iOS底层原理 - 常驻线程相关推荐

  1. iOS底层原理之内存管理

    文章目录 定时器 CADisplayLink.NSTimer GCD定时器 内存管理 iOS程序的内存布局 Tagged Pointer OC对象的内存管理 拷贝 引用计数的存储 dealloc 自动 ...

  2. iOS底层原理之架构设计

    文章目录 何为架构? MVC - Apple版 MVC – 变种 MVP MVVM 设计模式 面试题 何为架构? 架构(Architecture):软件开发中的设计方案,类与类之间的关系.模块与模块之 ...

  3. iOS底层原理探究 第一探. 事件传递和响应者链

    一. 声明:  本文意在探讨, 也参考了几位大神的文章, 在最后我会把链接发出来, 如果有理解错误的地方, 请大神们指正哈! 二. 前言:  最近自己做项目的时候, 用到了UITabbarContro ...

  4. 并发底层原理:线程、资源共享、volatile 关键字

    并发底层原理:线程.资源共享.volatile 关键字 1.线程 1.1 定义任务 1.2 Thread 类 1.3 使用 Executor 1.4 从任务中产生返回值 1.6 优先级 1.7 后台线 ...

  5. 视频教程-iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-iOS

    iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化 小码哥教育CEO,曾开发了2个iOS的流行开源框架(MJRefresh.MJExtension),目前在国内的使用率非常高. 李 ...

  6. iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-李明杰-专题视频课程...

    iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-236人已学习 课程介绍         得遇名师,突飞猛进!iOS培训王者MJ(李明杰)老师精心研发,iOS进阶课程,实用技术 ...

  7. iOS底层原理总结 - OC对象的本质

    苹果官方文档 The Objective-C language defers as many decisions as it can from compile time and link time t ...

  8. iOS底层原理班实战视频教程 -李明杰-专题视频课程

    IOS底层开发班实战视频培训课程:APP逆向实战.加壳脱壳.数据安全.编译原理.iOS底层开发实现.iOS底层开发机制 iOS进阶课程,实用技术不断的更新和升级,更快帮助职场人士在开发领域脱颖而出.远 ...

  9. iOS底层原理探究-Runloop

    Runloop 1. 概述 一般来说,一个线程只能执行一个任务,执行完就会退出,如果我们需要一种机制,让线程能随时处理时间但并不退出,那么 RunLoop 就是这样的一个机制.Runloop是事件接收 ...

最新文章

  1. uploadify 上传
  2. java enum(枚举)的使用
  3. xampp打开mysql的admin访问被拒绝_我被我的电脑磁盘拒绝了,为什么打不开,而显示“拒绝访问”呢...
  4. 用C++实现的壳(基础版)
  5. 结构与算法(05):二叉树与多叉树
  6. 大数据相加_推动媒体融合与大数据相加发展
  7. codevs 3287 货车运输 NOIP2013提高组
  8. 从理论上来说,国足是否还有出线的可能性?分析数据后给你答案
  9. P3 如何创建数据库数据表
  10. linux+arm+移除X11,关于ARM GTK/X11的问题,懂的指点一下
  11. 第四章节 窗体应用(windows应用程序)
  12. tomcat 在linux下的关闭问题
  13. python安装jupter在线ide
  14. 使用uni-app把h5网页封装成app
  15. 东北农业大学计算机科学与技术复试名单,更新!最新182所院校复试信息汇总
  16. EmguCV的学习日志(一)
  17. flppy bri_BRI的完整形式是什么?
  18. 坐标正算和坐标反算的c语言,坐标正算程序坐标反算程序
  19. bemusic使用帮助
  20. verdi\debussy的使用技巧

热门文章

  1. 开源:ASP.NET MVC+EF6+Bootstrap开发框架
  2. java io在文件结尾持续添加内容
  3. SLAM精度测评——rpg_trajectory_evaluatio
  4. Spring @bean冲突解决方案
  5. php扩展xdebug基本使用
  6. Xcache安装与使用
  7. 从*p++说指针,数组,结构和函数
  8. GCC编译选项--创建与使用库
  9. 使用man在线手册页
  10. C++11中= delete;的使用