浅尝iOS中RunLoop

今天和大家一起来学习一下RunLoop的基本使用,有疏忽的地方,还望各位不吝赐教。


一、RunLoop简介

RunLoop直接翻译过来就是运行循环。

  /*
   * 在UIApplicationMain内部其实启动了一个RunLoop 返回值是一个int类型
   * 所以UIApplicationMain函数一直没有返回,从而保证了程序的运行
   */
   int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
      }
  }
   /* 反正都写到这里了,顺带说一句:iOS程序启动做了啥?
     1、执行main函数
     2、执行UIApplicationMain,创建其对象,并设置其代理
     3、开启一个事件循环(主运行循环,死循环:保证应用程序不会退出)
     4、去加载info.plist(判断info.plist当中有没有Main,如果有加载Main.storyboard)
     5、应用程序启动完毕(通知代理应用程序启动完毕)
    */

作用

  • 保持程序的持续运行
  • 处理app的各种事件(比如触摸事件、定时器事件、selector事件)
  • 节省CPU资源,提高程序的性能:该做事做事,该休息休息

API

  • Foundation框架
    NSRunLoop:NSRunLoop是对CFRunLoopRef一层封装。
  • coreFoundation框架
    CFRunLoopRef

二、RunLoop和线程的关系

1、一一对应的关系 是通过字典的形式进行关联的 线程为Key RunLoop为value来保存的
2、主线程的RunLoop已经自动创建好了,子线程的需要自行创建
3、RunLoop在第一次获取时创建,在线程结束时销毁

    // 获得主线程对应的RunLoop
    [NSRunLoop mainRunLoop];
    CFRunLoopGetMain();
    // 获得当前线程的RunLoop
    [NSRunLoop currentRunLoop];
    CFRunLoopGetCurrent()
    // 相互转换
    mainRunLoop.getCFRunLoop;
    // 主线程的RunLoop已经自动创建好了,子线程的需要自行创建
    [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    // run方法的实现
      - (void)run{
          // 创建子线程对应的RunLoop使用currentRunLoop方法来创建,懒加载
          NSLog(@"NSRunLoop-------------------%@",[NSRunLoop currentRunLoop]);
          NSLog(@"NSThread-------------------%@",[NSThread currentThread]);
      }

三、RunLoop相关类和运行模式

CoreFoundation中关于RunLoop的五个类

CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

系统的5个Mode

kCFRunLoopDefaultMode:App默认的Mode,通常主线程在这个Mode下运行
UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他mode的影响
UIInitializationRunLoopMode:用不到,程序刚启动进入的的第一个Mode,启动后就不再使用
GSEventReceiveRunLoopMode:接收系统事件的内部Mode,用不到
kCFRunLoopCommonModes:这是一个占位的Mode,不是一种真正的Mode

  • RunLoop中有多个运行模式,但是只能选择一种模式运行
  • 每次RunLoop启动时,只能指定其中的一个Mode,这个Mode被称作CurrentMode
  • 如果要切换Mode,只能退出RunLoop,再重新指定Mode进入,这样做是为了分隔开不同组的Source/timer/Observer,互不影响
  • Mode中至少包含一个timer或者是Resource

四、RunLoop--CFRunLoopTimerRef

1、创建定时器方式一

    // 1、创建定时器
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    // 2、添加定时器到runloop,指定runloop为默认模式
    /*
     * 第一个参数:定时器
     * 第二个参数:RunLoop的运行模式
       * NSDefaultRunLoopMode 如果使用本模式,当界面中有拖拽操作的时候,定时器不会工作,因为RunLoop切换了运行模式为 UITrackingRunLoopMode 界面追踪模式
       * UITrackingRunLoopMode 如果使用本模式,当界面中有拖拽操作的时候,定时器才会工作。
       * 如果想两种模式同时使用就都添加或者使用NSRunLoopCommonModes
       * NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode
       * 相当于把timer添加到NSDefaultRunLoopMode模式下,又添加到UITrackingRunLoopMode模式下面
       * NSRunLoopCommonModes并不是真正意义上的模式,用来占位用的,本身是个标签 凡是添加到NSRunLoopCommonModes中的事件同时会被添加到打上Common标签的运行模式上
     */
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    // 3、实现run方法
    - (void)run{

        NSLog(@"run-------%@--%@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode);
    }

2、创建定时器方式二

    // 此方法会自动添加RunLoop中,并且设置运行模式为默认
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

3、在子线程调用定时器

    // 利用NSThread开子线程
    [NSThread detachNewThreadSelector:@selector(timer) toTarget:self withObject:nil];
    // 注意子线程中的RunLoop要手动创建 这里是timer方法的实现
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    // 给方法会自动添加RunLoop中,并且设置运行模式为默认
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    // 开启RunLoop
    [runloop run];

五、RunLoop--CFRunLoopSourceRef

/*
 * CFRunLoopSourceRef是事件源(输入源)
 * 以前的分类
 * Port——base Source
 * Custom Input Source
 * Cocoa Perform Selector Source
 *
 * 现在的分类 函数调用栈
 * Source0:非基于Port的 用户自定义的
 * Source1:基于Port的 系统的
 */

六、RunLoop--CFRunLoopObserverRef

/*
 * CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
 * 可以监听的时间点有以下几个
 * kCFRunLoopEntry // 即将进入RunLoop
 * kCFRunLoopBeforeTimer // 即将处理Timer 
 * kCFRunLoopBeforeSources // 即将处理Sources
 * kCFRunLoopBeforeWaiting // 即将休眠
 * kCFRunLoopAfterWaiting // 即将唤醒
 * kCFRunLoopExit // 即将退出
 */
     /*
     * 第一个参数:怎么分配存储空间 kCFAllocatorDefault 默认方式分配
     * 第二个参数:要监听的状态 kCFRunLoopAllActivities 所有的状态
     * 第三个参数:是否持续监听 yes表示持续监听
     * 第四个参数:优先级 总是传递0
     * 第五个参数:当状态改变时候的回调
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        /*
        typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
            kCFRunLoopEntry = (1UL << 0), 即将进入RunLoop
            kCFRunLoopBeforeTimers = (1UL << 1), 处理Timer事件
            kCFRunLoopBeforeSources = (1UL << 2), 处理Source事件
            kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入睡眠
            kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒
            kCFRunLoopExit = (1UL << 7), RunLoop退出
            kCFRunLoopAllActivities = 0x0FFFFFFFU 所有状态
        };
         */
        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;
        }
});
    
    /*
     * 第一个参数:要监听哪个RunLoop
     * 第二个参数:观察者
     * 第三个参数:运行模式
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
//    NSRunLoopCommonModes == kCFRunLoopCommonModes
//     NSDefaultRunLoopMode == kCFRunLoopDefaultMode

七、RunLoop应用

/*
 * RunLoop的应用(当然关于RunLoop的应用当然不止这些,笔者也会多多学习,在以后的文章中分享)
 * NSTimer(上面进行了尝试)
 * 常驻线程(下面展示)
 * 自动释放池(这个就算了,笔者认知也有限,福利一下面试)
     自动释放池什么时候释放?
     * RunLoop第一次创建:即将启动的时候创建
     * RunLoop最后一次销毁:即将退出的时候
     * 其他时间创建和销毁:当RunLoop即将睡眠的时候销毁之前的释放池,重新创建一个新的自动释放池
 */
 /** 线程属性 我在界面中添加了两个按钮,一个用于创建线程,一个 用于继续使用当前线程继续执行任务*/
@property (nonatomic, strong) NSThread *thread;
 /** 创建线程按钮执行的方法 */
  self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(task1) object:nil];
  [self.thread start];
/** task1任务实现 */
- (void)task1{
    // 同一个线程中任务是串行执行的
    NSLog(@"执行task1");

    // 解决方法,开一个RunLoop,如果没有这个RunLoop self.thread会进入死亡状态,无法再继续执行任务2
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    
    // 如果没有timer或者source 添加保证不让RunLoop退出
    [runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
    
    // 开启
    //    [runloop run];
    // 仅限于子线程的RunLoop,主线程没有效果
    [runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
}
 /** 继续执行任务按钮方法 */
  // 直接调动会崩溃,因为虽然self.thread被强指针指向,但是当任务1执行完毕后,self.thread已经进入死亡状态,无法再继续执行任务2
   [self performSelector:@selector(task2) onThread:self.thread withObject:nil waitUntilDone:YES];
 /** task2任务实现 */
  - (void)task2{

      NSLog(@"执行task2");
  }

写在最后的话:关于iOS-RunLoop的知识今天就学习到这里,关于iOS-RunLoop方面的问题欢迎大家和我交流,共同进步,谢谢各位。

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

推荐阅读更多精彩内容