美文网首页
运行循环

运行循环

作者: sajiner | 来源:发表于2017-06-27 23:00 被阅读17次

    前言

    趁辞职还未上班的当,对项目中所使用到的技术做下总结。一来,温故知新,二来,希望也能帮到需要的人。废话不多说,请往下看:

    概念:

    1. 保证应用程序不退出
    2. 负责所有事件的监听
    3. 如果没有事件发生,运行循环会进入休眠状态,待事件发生时,再重新启动
    4. 子线程的运行循环默认是不启动的

    内部实现:

    一般而言,一个线程只执行一个任务,任务完成后,线程就会退出。runloop提供了这样一种机制:让线程能随时处理事件且不退出。见代码:

    void click(int type) {
        NSLog(@"正在处理 %d", type);
    }
    // 在 iOS 程序中,主线程的运行循环是默认启动的,不需要程序员手动创建或者启动!
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            while (YES) {
                NSLog(@"请输入选项,0 退出");
                
                int result = -1;
                // 阻塞式函数,如果用户不输入,就不会继续后续的代码!
                scanf("%d", &result);
                
                NSLog(@"Hello, World! %d", result);
                
                if (result == 0) {
                    break;
                } else {
                    // 发生事件,处理事件
                    click(result);
                }
            }
        }
        return 0;
    }
    

    与线程的关系

    runloop 是为线程而生的,没有线程,就没有存在的必要; run loops 是线程的基础架构部分,Cocoa 和 CoreFoundation 都提供了 run loop 对象方便配置和管理线程的 run loop ;每个线程,包括主线程,都有与之对应的 run loop 对象

    1. 主线程的run loop默认是启动的
    2. 对其它线程来说,run loop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要
    3. 在任何一个 Cocoa 程序的线程中,都可以通过 [NSRunLoop currentRunLoop] 来获取到当前线程的 run loop

    遇到的问题:

    • 以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?
    • 发生原因:
      - Runloop 只能运行在一个 mode 下,如果要换 mode ,当前的 loop 也需要停下重新启动成新的。
      - 此方法是以 NSDefaultRunLoopMode 模式添加到运行循环的,优先级:NSRunLoopCommonModes > NSDefaultRunLoopMode
      - 所以,滑动时 timer 会暂停
    • 一般的解决方法:替换以上方法
    - (void)viewDidLoad {   
       [super viewDidLoad];
        
        self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
        // 表示发生用户交互的时候,时钟仍然会触发!
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
        
        // 调度时钟,默认将时钟以 NSDefaultRunLoopMode 模式添加到运行循环
    //    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
    }
    
    - (void)updateTimer {
        static int num = 0;
        
        // 模拟延时
        [NSThread sleepForTimeInterval:1.0];
        
        NSLog(@"%d", num++);
    }
    

    但是此方法针对时钟触发的方法非常耗时时,使用 NSRunLoopCommonModes 就会非常卡顿

    • 改进的解决方案:
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // 在子线程开启时钟!由于子线程的运行循环没有启动,所以没法监听时钟事件
            self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
            [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
            
            // 开启运行循环 - 启动运行循环 - 这句代码就是一个死循环!
            // 如果不停止运行循环,不会执行后续的任何代码
            // 一旦停止了运行循环,后续代码能够执行,执行完毕后,线程被自动销毁!
            CFRunLoopRun();
            
            NSLog(@"come here");
        });
    }
    
    - (void)updateTimer {
        static int num = 0;
        
        // 模拟延时
    //    [NSThread sleepForTimeInterval:1.0];
        
        NSLog(@"%d %@", num++, [NSThread currentThread]);
        
        // 满足条件后,停止当前的运行循环
        if (num == 8) {
            // 一旦停止了运行循环,后续代码能够执行,执行完毕后,线程被自动销毁!
            CFRunLoopStop(CFRunLoopGetCurrent());
        }
    }
    

    有关mode

    • mode 主要是用来指定事件在运行循环中的优先级。分为:
      - NSDefaultRunLoopMode 默认,空闲状态
      - UITrackingRunLoopMode ScrollView滑动时
      - UIInitializationRunLoopMode 启动时
      - NSRunLoopCommonModes Mode集合
    • 苹果公开提供的 mode 有两个
      - NSDefaultRunLoopMode
      - NSRunLoopCommonModes

    其他用途

    相关文章

      网友评论

          本文标题:运行循环

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