RunLoop

什么是RunLoop ?

  • 可以把RunLoop当做是一个无限的死循环,正因为有了这种循环,程序才会在执行完一遍后也不会挂掉,如果没有杀死程序进程,程序会一直监听app中的各种事件(比如触摸事件,定时器事件,selector事件)但不同于死循环的是RunLoop会在事件监听的过程中处于休眠状态,该做事时做事,该休息时休息,这样可以节省cpu资源,提高程序性能
  • 一个线程对应一个RunLoop,主线程RunLoop默认已经开启,而子线程的RunLoop需要手动开启(调用run方法)子线程结束时RunLoop销毁

获取RunLoop对象 Foundation

[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

获取RunLoop对象 Core Foundation

    CFRunLoopGetCurrent();  // 获得当前线程的RunLoop对象
    CFRunLoopGetMain();    //获得主线程的RunLoop对象

RunLoop结构

Paste_Image.png
  • Core Foundation中关于RunLoop的五个类
    CFRunLoopRef
    CFRunLoopModeRef
    CFRunLoopSourceRef
    CFRunLoopTimerRef
    CFRunLoopObserverRef
  • CFRunLoopModeRef代表RunLoop的运行模式
  • 一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer
  • 每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode
  • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
  • 这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响

CFRunLoopModeRef

系统默认注册了5个mode

  • NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行
  • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  • UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
  • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
  • NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode,从表面上看这是一个models,也就是一个模式的数组包含默认模式和界面跟钟模式,实际上他会处理两种模式下同时产生的各种事件

CFRunLoopTimerRef

CFRunLoopTimerRef是基于时间的触发器
CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响
GCD的定时器不受RunLoop的Mode影响

CFRunLoopSourceRef (事件源 输入源)

按照官方文档,Source的分类
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources

CFRunLoopObserverRef

(CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变)

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),                    //即将进入RunLoop
    kCFRunLoopBeforeTimers = (1UL << 1),      //即将处理Timer
    kCFRunLoopBeforeSources = (1UL << 2),    //即将处理Sources
    kCFRunLoopBeforeWaiting = (1UL << 5),    //即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),       //刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7),                        //即将退出RunLoop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

给主线程runloop添加观察者

// 1.创建一个监听对象
    /*
     第一个参数: 告诉系统如何给Observer对象分配存储空间
     第二个参数: 需要监听的类型
     第三个参数: 是否需要重复监听
     第四个参数: 优先级
     第五个参数: 监听到对应的状态之后的回调
     */
    
    CFRunLoopObserverRef observer =  CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        //        NSLog(@"%lu", activity);
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"进入RunLoop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"即将处理timer");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"即将处理source");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"即将进入睡眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"刚刚从睡眠中醒来");
                break;
            case kCFRunLoopExit:
                NSLog(@"退出RunLoop");
                break;
                
            default:
                break;
        }
    });
    
    // 2.给主线程的RunLoop添加监听
    /*
     第一个参数:需要监听的RunLoop对象
     第二个参数:给指定的RunLoop对象添加的监听对象
     第三个参数:在那种模式下监听
     */
   
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
   
    //3.释放Observer
    CFRelease(observer);

RunLoop处理逻辑-官方版

Paste_Image.png

RunLoop处理逻辑-网友整理

Paste_Image.png

常驻线程

应用场景:开发中要频繁的执行某一个耗时操作,通常会把这个耗时的操作放在子线程中执行,而系统默认子线程中任务执行完后这条子线程就会销毁,
注意 这里千万不要以为只要使用全局属性强引用这条线程就可以继续使用这条线程了,
测试代码: 1/2

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
    self.thread.name = @"我是常驻线程";
    [self.thread start];
}

- (void)demo
{
    //  获取当前线程
    NSLog(@"%@--%d",[NSThread currentThread],__LINE__);

    // 获取当前runLoop对象
     NSRunLoop *runLoop =[NSRunLoop currentRunLoop];
    // 以下代码的目的是为了保证runloop不死   一个NSRunLoop中, 如果没有source或者timer, 那么NSRunLoop就会退出死循环
     [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
     //  运行runLoop对象
    [runLoop run];
    
    NSLog(@"如果开启运行循环  我不会被打印出来");
}

打印结果:

Paste_Image.png

测试代码: 2/2

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //  获取当前线程
    NSLog(@"%@--%d",[NSThread currentThread],__LINE__);
    // 主线程
   [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}

- (void)test
{
    //  获取当前线程
    NSLog(@"%@--%d",[NSThread currentThread],__LINE__);
    NSLog(@"已确认可以继续使用这条线程做事");
}

打印结果

**2016-04-07 14:34:37.858 08-RunLoop****应用场景****[3069:184521] <NSThread: 0x7fb60ac08a10>{number = 1, name = main}****--****49**
**2016-04-07 14:34:37.859 08-RunLoop****应用场景****[3069:184863] <NSThread: 0x7fb60af4af30>{number = 2, name = ****我是常驻线程****}****--****57**
**2016-04-07 14:34:37.859 08-RunLoop****应用场景****[3069:184863] ****已确认可以继续使用这条线程做事**
Paste_Image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容

  • 如果没有RunLoop main函数中的RunLoop 第14行代码的UIApplicationMain函数内部就...
    JonesCxy阅读 522评论 0 4
  • 1 RunLoop简介 神秘的RunLoop。一个应用开始运行以后放在那里,如果不对它进行任何操作,这个应用就像静...
    Claire_wu阅读 1,756评论 3 30
  • RunLoop 文章目录 RunLoop简介 1.1 什么是RunLoop? 1.2 RunLoop和线程 1.3...
    May_d8f1阅读 280评论 0 1
  • 一、什么是runloop 字面意思是“消息循环、运行循环”。它不是线程,但它和线程息息相关。一般来讲,一个线程一次...
    WeiHing阅读 8,106评论 11 111
  • 夕阳西下,别再去留恋今日的温暖,因为明天太阳依然会升起。 摄于2017年2月26日
    书香会阅读 216评论 0 1