iOS多线程篇-RunLoop

RunLoop

简述

1、RunLoop是事件接收和分发机制的一个实现
2、并且它能处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
3、以及节省CPU资源,提高程序性能:(该做事时做事,该休息时休息)

如何获取Runloop对象:

这里的话IOS提供了两套API来访问或使用RunLoop
    1、CFRunLoopRef      是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。
    2、NSRunLoop         是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。

CFRunLoopRef的代码是开源的,你可以在这里CFRunLoopRef源码下载到整个 CoreFoundation 的源码

每一个线程对应着一个RunLoop,但是线程在创建的时候是没有RunLoop的,如果你不去获取它,它会一直没有,当然必须你自己的主动去获取,但是在你线程结束的时候,你所获取的RunLoop也跟着销毁了。如果你需要在某个线程对你自己的RunLoop执行一些事件的时候,那么你就的在线程未结束之前进行操作,然而在程序中是具有一个主RunLoop的,它用来管理程序的生死,具体的话是在UIApplicationMain里面执行

//具体显示
int main(int argc, char * argv[]) {
    @autoreleasepool {
        //程序开始执行  会输出这段语句
        NSLog(@"------------------");
        //可以看出这里面是一直执行的  相当于一个死循环
        int result = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        //程序结束了才会执行  会输出这段语句,在未结束之前这句话是不会执行的
        NSLog(@"++++++++++++++++++");
        return result;
    }
}

获取的方式

//获取的两种方式

1、这种为CFRunLoopRef中的
    CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
    CFRunLoopGetMain(); // 获得主线程的RunLoop对象

2、这种为NSRunLoop中的
    [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
    [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

相关类

//相关的五个类

1、CFRunLoopRef
    1、代表一个RunLoop对象
2、CFRunLoopModeRef
    1、代表RunLoop的运行模式
        1、一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer
        2、每次RunLoop启动时,只能指定其中一个 Mode,如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
        3、同一时刻只能进行一种模式
    2、苹果内部提供了五种模式
        1、kCFRunLoopDefaultMode (NSDefaultRunLoopMode)
            1、App的默认Mode,通常主线程是在这个Mode下运行
        2、UITrackingRunLoopMode
            1、界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
        //这个通常用不到
        3、UIInitializationRunLoopMode
            1、在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
        //这个通常用不到
        4、GSEventReceiveRunLoopMode
            1、接受系统事件的内部 Mode
        5、kCFRunLoopCommonModes
            1、这是一个占位用的Mode,这个的话用语言很难表达,后面会看到实例中会使用到这里,大家仔细体会
3、CFRunLoopSourceRef
    1、用来管理所有事件的事件源,包括自定义的事件,以及系统自带的事件。
    2、Source有两个版本:Source0 和 Source1
        1、Source0-----为用户主动触发的事件
        2、Source1-----通过内核和其他线程相互发送消息。
4、CFRunLoopTimerRef
    1、基本上说的就是NSTimer,基本用法如下实例标示
5、CFRunLoopObserverRef
    1、用来监听RunLoop的状态改变
    2、状态列表
        kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
        kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
        kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
        kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
        kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
        kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
        kCFRunLoopAllActivities = 0x0FFFFFFFU //所有状态

补充

一个RunLoop有很多Mode,一个Mode里面有很多得Source/Timer/Observer,但是同一时刻只能进行一种模式。 如图:

实例

  • CFRunLoopTimerRef

  • 首先在我们的storyboard添加一个text view控件
    然后使用代码

  • 代码

    -(void)viewDidLoad {
        [super viewDidLoad];
        //在原来使用time的时候,我们是直接这样写的,它是直接添加到RunLoop的DefaultMode模式中去得,如果我们去滑动text view的时候,也就是说我们现在操作的是RunLoop的Tracking,因为在前面我们并没有把time添加到Tracking中去,那么滑动的时候是不会输出的,
        //创建time
        NSTimer *time = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
        //在这里我们把time添加到Tracking中去,那么操作就会发现,默认的是时候它也会输出,滑动text view的时候他也会输出了
        [[NSRunLoop currentRunLoop]addTimer:time forMode:UITrackingRunLoopMode];
    }
    -(void)show{
        NSLog(@"%s",__func__);
    }
    

    把time添加到NSDefaultRunLoopMode模式下

    NSTimer *time = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
     //在这里的话可以看到,我们滑动的时候它并没有输出,因为我们forMode的为NSDefaultRunLoopMode,也就是通常主线程的这个Mode下运行
    [[NSRunLoop currentRunLoop] addTimer:time forMode:NSDefaultRunLoopMode];
    

    把time添加到UITrackingRunLoopMode模式下

    NSTimer *time = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
    //    在这里的话可以看到,我们滑动的时候它才会输出,因为我们forMode的为UITrackingRunLoopMode,
    [[NSRunLoop currentRunLoop] addTimer:time forMode:UITrackingRunLoopMode];
    

    看了上面两种是不是有种感觉为什么两者不能共存,那么下面这种就可以看到它们共存了

    NSTimer *time = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(show) userInfo:nil repeats:YES];
    //    在这里的话可以看到,我们不管是程序启动还是滑动的时候它都会输出,因为我们forMode的为kCFRunLoopCommonModes,
    [[NSRunLoop currentRunLoop] addTimer:time forMode:kCFRunLoopCommonModes];
    
  • 补充

    关于定时器的话是有两种的一个是NSTime,但是它是会受RunLoop的模式所影响的,一个是GCD的定时器,它呢是不受RunLoop的模式所影响的,这里的话留给大家一个引子(GCD的定时器是如何不受RunLoop模式的影响),这个也是公司一般很爱问的一个问题。

  • CFRunLoopObserverRef

  • 代码

    - (void)viewDidLoad {
        [super viewDidLoad];
        /*
            第一个参数:指定如何给obsever分配存储空间
            第二个参数:需要监听的类型/kCFRunLoopAllActivities为全部
            第三个参数:是否每次都监听
            第四个参数:优先级
            第五个参数:监听状态改变之后的回调函数
         */
        CFRunLoopObserverRef obsever = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    //        kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
    //        kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
    //        kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
    //        kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
    //        kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
    //        kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
    //        kCFRunLoopAllActivities = 0x0FFFFFFFU //所有状态
            switch (activity) {
                case kCFRunLoopEntry:
                    NSLog(@"即将进入Loop");
                    break;
                case kCFRunLoopBeforeTimers:
                    NSLog(@"即将处理 Timer");
                    break;
                case kCFRunLoopBeforeSources:
                    NSLog(@"即将处理 Source");
                    break;
                case kCFRunLoopBeforeWaiting:
                    NSLog(@"即将进入休眠");
                    break;
                case kCFRunLoopAfterWaiting:
                    NSLog(@"刚从休眠中唤醒");
                    break;
                case kCFRunLoopExit:
                    NSLog(@"即将退出Loop");
                    break;
                default:
                    break;
            }
        });
        //给主线程的RunLoop添加一个观察者
        /*
         第一个参数:需要给那个RunLoop添加观察者
         第二个参数:需要添加的observer
         第三个参数:在那种模式下监听
         */
        CFRunLoopAddObserver(CFRunLoopGetMain(), obsever,kCFRunLoopDefaultMode );
        CFRelease(obsever);
    }
    
  • 补充
    这里的话如果打印出来,是会具有很多time和Source的,因为苹果内部进行了一系列的调用,那么大家可以明显的看到,这里是如何监听RunLoop状态是如何改变的,最后一定要记得去release,因为ARC无法释放Core Foundation 框架中的Create、Copy、Release

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

推荐阅读更多精彩内容

  • 基本概念 进程 进程是指在系统中正在运行的一个应用程序,而且每个进程之间是独立的,它们都运行在其专用且受保护的内存...
    小枫123阅读 883评论 0 1
  • RunLoop的基本了解 **1 . RunLoop字面的意思 : **运行循环 / 跑圈 **2 . 基本作用 ...
    Mario_ZJ阅读 505评论 1 3
  • RunLoop NSRunLoop是IOS消息机制的处理模式 主要作用 一条线程对应一个RunLoop,主线程的R...
    大冲哥阅读 211评论 0 0
  • ======================= 前言 RunLoop 是 iOS 和 OSX 开发中非常基础的一个...
    i憬铭阅读 867评论 0 4
  • 认识 Runloop Runloop 就是运行循环,如果没有 Runloop,程序一运行就会退出,有 Runloo...
    BWLi420阅读 1,569评论 0 19