美文网首页程序员
ios RunLoop与线程

ios RunLoop与线程

作者: wp_Demo | 来源:发表于2018-12-21 17:08 被阅读0次
关系
  • 线程与RunLoop是一一对应的
  • 线程创建的时候,并没有创建RunLoop对象,RunLoop会在第一次获取的时候自动创建
  • 主线程默认开启的RunLoop,子线程需要主动创建
runloop的使用:
  1. Core Foundation: CFRunLopRef(开源)
  2. 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;
}

结果表明是可以停止的,当点击之后

相关文章

网友评论

    本文标题:ios RunLoop与线程

    本文链接:https://www.haomeiwen.com/subject/icfvkqtx.html