关系
- 线程与RunLoop是一一对应的
- 线程创建的时候,并没有创建RunLoop对象,RunLoop会在第一次获取的时候自动创建
- 主线程默认开启的RunLoop,子线程需要主动创建
runloop的使用:
- Core Foundation: CFRunLopRef(开源)
- Foundation: NSRunLoop
CFRunLopRef没有提供创建runloop的方法,只提供了获取的方法:
CFRunLoopRef mainRunLoop = CFRunLoopGetMain();
CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
我们翻阅一下CFRunLoop发现:
获取主RunLoop
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
然后在_CFRunLoopGet0的方法中可以看到(这里删减了一些代码):
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
//__CFRunLoops是全局的字典
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
//如果__CFRunLoops为空的话,创建字典
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//创建主runloop:mainLoop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//把mainLoop存到字典中,把线程做为key,mainLoop作为值
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
}
//从全局字典里面取主runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
//如果没有取到,重新创建并存到字典里面去
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
return loop;
}
从上面可以得出,线程与RunLoop确实是一一对应的,因为使用了全局字典保存了起来,保证了一一对应
定时器:
WPThread *thread = [[WPThread alloc]initWithBlock:^{
//定时器的定时回调是由runloop进行回调处理的,现在是没有处理的情况
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"定时器执行");
}];
NSLog(@"任务做完了");
}];
[thread start];
2018-12-21 16:53:03.812768+0800 runloop与线程的关系[17292:568527] 任务做完了
2018-12-21 16:53:03.813107+0800 runloop与线程的关系[17292:568527] dealloc
结果定时器没有执行到
但是当我们开启runloop
WPThread *thread = [[WPThread alloc]initWithBlock:^{
//定时器的定时回调是由runloop进行回调处理的,现在是没有处理的情况
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"定时器执行");
}];
NSLog(@"任务做完了");
[[NSRunLoop currentRunLoop] run];
}];
[thread start];
2018-12-21 16:55:23.783311+0800 runloop与线程的关系[17329:573207] 任务做完了
2018-12-21 16:55:24.787985+0800 runloop与线程的关系[17329:573207] 定时器执行
2018-12-21 16:55:25.786954+0800 runloop与线程的关系[17329:573207] 定时器执行
2018-12-21 16:55:26.783332+0800 runloop与线程的关系[17329:573207] 定时器执行
定时器执行了,但是这里貌似没有办法停止循环,如果我们写一个点击事件,在点击事件里面停止线程的话,会直接停止主线程,这样是不合适的,下面是我的解决办法:
@interface ViewController ()
@property (nonatomic,strong)WPThread *thread;
@property (nonatomic,assign)BOOL needStop;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[WPThread alloc]initWithBlock:^{
//定时器的定时回调是由runloop进行回调处理的,现在是没有处理的情况
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"定时器执行");
if (self.needStop == YES) {
if ([[NSThread currentThread]isMainThread] == YES) {
NSLog(@"在主线程中");
}else{
NSLog(@"在子线程中");
}
[NSThread exit];//把当前的线程停止,当前线程不是主线程
}
}];
NSLog(@"任务做完了");
[[NSRunLoop currentRunLoop] run];
}];
[self.thread start];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.needStop = YES;
}
结果表明是可以停止的,当点击之后
网友评论