RunLoop

作者: 曹来东 | 来源:发表于2018-09-07 09:19 被阅读8次

    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.png

    CFRunLoopModeRef

    • 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.png

    RunLoop的运行逻辑

    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];
        
    

    NSRunLoopCommonModeskCFRunLoopDefaultModeUITrackingRunLoopMode模式下都可正常运行.NSRunLoopCommonModes不是一个真正的模式,只是一个标记.kCFRunLoopDefaultModeUITrackingRunLoopMode都被默认标记为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
    

    相关文章

      网友评论

          本文标题:RunLoop

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