分享

RunLoop(一)

 昵称44248756 2017-07-06
一、什么是RunLoop
(1)字面意思:运行循环/跑圈。
(2)基本作用:保持程序的持续运行;
                             处理APP中的各种事件(比如触摸事件、定时器事件、Selector事件);
                             节省CPU资源,提高程序性能(该做事时做事,该休息时休息);
                              ......
(3)如果没有RunLoop,第三行后程序就结束了。
 (4)如果有了RunLoop,由于main函数里面启动了个RunLoop,所以程序并不会马上退出,保持持续运行状态。
 (5)main函数中的RunLoop,第14行代码的UIApplicationMain函数内部就启动了一个RunLoop,所以UIApplicationMain函数一直没有返回,保持了程序的持续运行,这个默认启动的RunLoop是跟主线程相关联的 

二、RunLoop对象
(1)iOS中有两套API来访问和使用RunLoop对象:

1)Foundation
      NSRunLoop2)Core Foundation
      CFRunLoopRef

(2)NSRunLoop和CFRunLoopRef都代表着RunLoop对象;
(3)NSRunLoop是基于CFRunLoopRef的一层OC的包装,所以要了解着RunLoop的内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面);
(4)RunLoop是字典存储,key是线程,value是runloop;
1、获取RunLoop对象
(1)Foundation

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

(2)Core Foundation

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

2、RunLoop相关类
(1)Core Foundation中关于RunLoop的5个类

1CFRunLoopRef2CFRunLoopMode3CFRunLoopSourceRef4CFRunLoopTimerRef5CFRunLoopObserverRef

(2)CFRunLoopMode


1432798883604537.png
1)CFRunLoopMode代表RunLoop的运行模式
(2)一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer
(3)每次RunLoop启动时,只能呢指定其中一个Mode,这个Mode被称为CurrentMode
(4)如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这么做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响;
(5)系统默认注册了5Mode:
    kCFRunLoopDefaultMode :App的默认Mode,通常主线程是在这个Mode下运行;
    UITrackingRunLoopMode :界面跟踪Mode,用于scrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响;
    UIInitializationRunLoopMode :在启动App时进入的第一个Mode,启动完成后就不再使用;
    GSEventReceiveRunLoopMode :接受系统事件的内部Mode,通常用不到;
    kCFRunLoopCommonModes :这是一个用于占位用的Mode,不是一种真正的 Mode

(3) CFRunLoopTimerRef
CFRunLoopTimerRef是基于时间的触发器,基本上说的就是NSTimer:

NSTimer*timer=[NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//只运行在NSDefaultRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

//只运行在UITrackingRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

//定时器会跑在标记为common modes的模式下
//标记为common modes的模式 :UITrackingRunLoopMode和kCFRunLoopDefaultMode
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

//调用了scheduledTimer返回的定时器,已经自动被添加到当前的runLoop中,而且是NSDefaultRunLoopMode
NSTimer*timer=[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//修改模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

那么在滑动页面上的列表时, timer 会暂定回调,为什么?如何解决?
原因:
timer 默认是运行在 NSDefaultRunLoopMode 模式下的,当滑动页面上的列表时,进入了 UITrackingRunLoopMode 模式,这时候 timer 就会停止。
办法:
修改 timer 的运行模式为 NSRunLoopCommonModes,这样定时器就可以一直运行了。
补充:
1》在子线程中通过 scheduledTimerWithTimeInterval:...方法来构建NSTimer,方法内部已经创建 NSTimer 对象,并加入到 RunLoop 中,运行模式为NSDefaultRunLoopMode,由于 Mode 有 timer 对象,所以 RunLoop 就开始监听定时器事件了,从而开始进入运行循环,这个方法仅仅是创建 RunLoop 对象,并不会主动启动 RunLoop,需要再调用 run方法来启动;
2》如果在主线程中通过 scheduledTimerWithTimeInterval:...方法来构建 NSTimer,就不需要主动启动 RunLoop 对象,因为主线程的 RunLoop 对象在程序运行起来就已经被启动了;

//userInfo参数:用来给NSTimer的userInfo 属性赋值,userInfo 是只读的,只能在构建 NSTimer 对象时赋值
[NSTimer scheduledTimerWithTimeInterval:1.0 target:selfselector:@selector(run:) userInfo:@"ya 了 个 hoo"repeats:YES];

// scheduledTimer...方法创建出来 NSTimer 虽然已经指定了默认模式,但是【允许你修改模式】
[[NSRunLoop currentRunLoop] addTimer:timerforMode:NSRunLoopCommonModes];

// 【仅在子线程】需要手动启动 RunLoop 对象,进入运行循环
[[NSRunLoop currentRunLoop] run];

(4)CFRunLoopSourceRef
CFRunLoopSourceRef是事件源(输入源)。
按照官方文档,Source的分类:

 (1)Port-Based Sources
 (2)Custom Input Sources
 (3)Cocoa Perform Selector Sources

按照函数调用栈,Source的分类:

1)Source() :非基于Port的
(2)Source1 :基于Port的,通过内核和其他线程通信,接收,分发系统事件

(5)CFRunLoopObserverRef
CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变。
可以监听的时间点有以下几个:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),//1 即将进入Loop
    kCFRunLoopBeforeTimers = (1UL << 1),// 2即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2),// 4即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5),//32 即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),// 64刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7),// 128即将退出Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};
//添加observer
CFRunLoopObserverRef observer=CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"监听到RunLoop状态的状态发生改变---%zd",activity);
});
//添加观察者:监听RunLoop状态
CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer, kCFRunLoopDefaultMode);
//释放observer
CFRelease(observer);

补充:
CF的内存管理(Core Foundation):
(1)凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象、都需要在最后做一次release;
(2)release函数: CFRelease(对象);

三、RunLoop 与线程
(1)每条线程都有唯一的一个与之对应的RunLoop对象;
(2)主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
(3)RunLoop在第一次获取时创建,在线程结束时销毁
四、RunLoop 处理逻辑
(1)官方版
(2)网友整理版
五、RunLoop 应用
(1)NSTimer
(2)ImageView显示
(3)PerformSelector
(4)常驻线程
(5)自动释放池

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多