RunLoop与线程Day19
- 每条线程都有唯一的一个与之对应的RunLoop对象
- RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
- 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
- RunLoop会在线程结束时销毁
- 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
获取RunLoop对象
- [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
- [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
- CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
- CFRunLoopGetMain(); // 获得主线程的RunLoop对象
RunLoop相关类
image.pngCFRunLoopModeRef
- CFRunLoopModeRef代表RunLoop的运行模式
- 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
- RunLoop启动时只能选择其中一个Mode,作为currentMode
- 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
- 不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
- 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
CFRunLoopModeRef
- kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
CFRunLoopObserverRef
image.png添加Observer监听RunLoop的所有状态
image.pngRunLoop的运行逻辑
Source0
触摸事件处理
performSelector:onThread:
Source1
基于Port的线程间通信
系统事件捕捉
Timers
NSTimer
performSelector:withObject:afterDelay:
Observers
用于监听RunLoop的状态
UI刷新(BeforeWaiting)
Autorelease pool(BeforeWaiting)
01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop
image.png
解决NSTimer在不同模式下失效问题
static int count = 0;
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%d",++count);
}];
如上代码是将NSTimer
添加到default
模式下.当垂涎滑动操作时,RunLoop会切换到UITrackingRunLoopMode
模式下,所以NSTimer
会失效.
static int count = 0;
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%d",count);
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
NSRunLoopCommonModes
在kCFRunLoopDefaultMode
和UITrackingRunLoopMode
模式下都可正常运行.NSRunLoopCommonModes
不是一个真正的模式,只是一个标记.kCFRunLoopDefaultMode
和UITrackingRunLoopMode
都被默认标记为NSRunLoopCommonModes
线程保活
typedef void (^LDPermenantThreadTask)(void);
@interface LDPermenantThread : NSObject
/**
开启线程
*/
//- (void)run;
/**
在当前子线程执行一个任务
*/
- (void)executeTask:(LDPermenantThreadTask)task;
/**
结束线程
*/
- (void)stop;
@end
#import "LDPermenantThread.h"
/** LDThread **/
@interface LDThread : NSThread
@end
@implementation LDThread
- (void)dealloc
{
NSLog(@"%s", __func__);
}
@end
@interface LDPermenantThread ()
@property (strong, nonatomic) LDThread *innerThread;
@end
@implementation LDPermenantThread
#pragma mark - public methods
- (instancetype)init
{
if (self = [super init]) {
self.innerThread = [[LDThread alloc] initWithBlock:^{
NSLog(@"begin----");
// 创建上下文(要初始化一下结构体)
CFRunLoopSourceContext context = {0};
// 创建source
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
// 往Runloop中添加source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 销毁source
CFRelease(source);
// 启动
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
// while (weakSelf && !weakSelf.isStopped) {
// // 第3个参数:returnAfterSourceHandled,设置为true,代表执行完source后就会退出当前loop
// CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
// }
NSLog(@"end----");
}];
[self.innerThread start];
}
return self;
}
//- (void)run
//{
// if (!self.innerThread) return;
//
// [self.innerThread start];
//}
- (void)executeTask:(LDPermenantThreadTask)task
{
if (!self.innerThread || !task) return;
[self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}
- (void)stop
{
if (!self.innerThread) return;
[self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}
- (void)dealloc
{
NSLog(@"%s", __func__);
[self stop];
}
#pragma mark - private methods
- (void)__stop
{
CFRunLoopStop(CFRunLoopGetCurrent());
self.innerThread = nil;
}
- (void)__executeTask:(LDPermenantThreadTask)task
{
task();
}
@end
//使用
#import "ViewController.h"
#import "LDPermenantThread.h"
@interface ViewController ()
@property (strong, nonatomic) LDPermenantThread *thread;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[LDPermenantThread alloc] init];
}
- (IBAction)stop:(id)sender {
[self.thread stop];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.thread executeTask:^{
NSLog(@"执行任务 - %@", [NSThread currentThread]);
}];
}
@end
网友评论