RunLoop

RunLoop

作用

  • 使程序一直运行并接收用户的输入
  • 决定程序在何时处理哪些事件
  • 调用解耦(Message Queue)
  • 节省CPU时间(当程序启动后,什么都没有执行的话,就不用让CPU来消耗资源来执行,直接进入睡眠状态)

模式

  • RunLoop 在同一段时间只能且必须在一种特定的模式下运行

  • 如果要更换 Mode,必须先停止当前的 Loop,然后再重新启动 Loop

  • Mode 是保证滚动流畅的关键

  • NSDefaultRunLoopMode:默认状态、空闲状态

  • UITrackingRunLoopMode:滚动模式,专门为滚动视图设计的

  • NSRunLoopCommonModes:默认包含以上两种模式

  • UIInitializationRunLoopMode:私有的,App启动时

模拟 RunLoop 实现

  • 准备的方法
void callFunc(int num) {
    NSLog(@"正在执行 %d 功能...",num);
}
  • main 方法中的实现
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        int result = 0;

        while (YES) {

            printf("请输入选项,O表示退出");

            // 阻塞式 : 需要控制台输入值才能继续运行
            scanf("%d",&result);

            if (0==result) {
                NSLog(@"程序正常退出");
                break;
            } else {
                callFunc(result);
            }
        }
    }
    return 0;
}

运行循环与时钟

  • 实际开发中,不建议将时钟的运行模式设置成 NSRunLoopCommonModes ,一旦耗时操作会影响流畅度
- (void)viewDidLoad {
    [super viewDidLoad];

    // 定时器 : 添加了滚动事件之后,定时器就不再执行了
//    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];

    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
    // timerWithTimeInterval 创建时钟时,需要将定时器添加到运行循环中
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}

时钟的执行方法入口

  • 模拟耗时操作 : 此处不能使用sleep, 因为线程休眠的时候事件循环不响应任何事件,开发中一定不要使用
- (void)fire
{
    // 不能使用 sleep
//    [NSThread sleepForTimeInterval:1.0];

    // 耗时操作
    for (int i = 0; i < 1000*1000; i++) {
        NSString *str = [NSString stringWithFormat:@"hello %d",i];
    }

    static int num = 0;
    num++;
    NSLog(@"%d",num);
}

运行测试,会发现卡顿非常严重

将时钟添加到子线程中工作

/// 线程
@property (nonatomic,strong) NSThread *thread;
- (void)viewDidLoad {
    [super viewDidLoad];

    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
    [self.thread start];
}
  • 注意 : 主线程的运行循环是默认启动的,但是子线程的运行循环是默认不工作的,这样能够保证线程执行完毕后,自动被销毁
- (void)demo
{
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    // 启动当前线程的运行循环;当前线程的运行循环是个不同于while的死循环
//    [[NSRunLoop currentRunLoop] run];
    CFRunLoopRun();

    NSLog(@"over");
}
  • 停止运行循环
// 停止当前运行循环
CFRunLoopStop(CFRunLoopGetCurrent());

按钮点击

创建按钮

- (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *addBtn = [UIButton buttonWithType:UIButtonTypeContactAdd];
    [self.view addSubview:addBtn];
    addBtn.center = self.view.center;

    // 向 runloop 注册监听,点击按钮,执行 ViewController 的 click 方法
    [addBtn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
}

点击方法

- (void)click
{
    NSLog(@"%s",__FUNCTION__);
}

时间循环图解按钮点击

图解按钮点击.png

RunLoop-主线程

  • 主线程的消息循环是默认开启.
  • 在主线程中使用定时源.即定时器.
  • 步骤 : 将定时源添加到当前线程的消息循环.

代码实现

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self timerDemo];
}

- (void)timerDemo
{
    // 创建定时器
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];

    // 将定时器添加到消息循环
    // currentRunLoop : 获取到当前的消息循环
    // forMode : 当前定时源timer的运行模式
    // NSRunLoopCommonModes : 模式组,里面包含了几种运行模式,kCFRunLoopDefaultMode / UITrackingRunLoopMode
    // 消息循环也是运行在一个模式下面的,默认的模式是kCFRunLoopDefaultMode,只有定时源的运行模式和消息循环的运行模式保持一致,定时源对应的方法才能执行
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

- (void)fire
{
    NSLog(@"hello %@",[NSRunLoop currentRunLoop].currentMode);
}

RunLoop-子线程

  • 子线程的消息循环是默认不开启.
  • 在子线程中使用定时源.即定时器.需要我们手动开启子线程的消息循环.
  • 步骤 : 将定时源添加到当前线程的消息循环.

代码实现

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//    [self timerDemo];

    NSLog(@"start");

    [self performSelectorInBackground:@selector(timerDemo) withObject:nil];
}

- (void)timerDemo
{
    //创建定时器(定时源)
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];

    // 获取当前的消息循环
    // currentRunLoop : 当前子线程的消息循环
    // 子线程的消息循环默认是不开启的,需要我们手动开启
    // 子线程中的运行模式和主线程是一样的
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    // 手动开启子线程的消息循环
    // run : 消息循环不停止的话,后面的代码永远不会被执行的
    // runUntilDate : 消息循环运行到指定的日期之后就自动的停止
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];

    //  提问 : over能够打印出来吗?
    NSLog(@"over");
}

- (void)fire
{
    NSLog(@"hello");
}

RunLoop.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容