学习博客:http://blog.ibireme.com/2015/05/18/runloop/
学习视频一:http://my.tv.sohu.com/pl/9137530/84240913.shtml
学习视屏二:http://my.tv.sohu.com/us/240760748/84240914.shtml
一、RunLoop简单概述
1)运行循环
内部实现:内部是由do-while循环实现的;
2)RunLoop作用
1、保证程序的持续运行;
2、处理APP的各种事件(滑动,定时器,selector);
3、节省cpu资源,提高程序性能;
3)RunLoop与线程
1、每条线程都有唯一的一个与之对应的RunLoop对象;
2、主线程的RunLoop随着程序已自动创建好,但是子线程的RunLoop需要手动创建;
3、获得主线程的RunLoop的方法是[NSRunLoop mainRunLoop];
4、创建子线程的RunLoop方法是[NSRunloop currentRunLoop];
***苹果不允许创建RunLoop,只是提供上述两种获得RunLoop的方法;
4)CFRunLoop的结构如下:
struct __CFRunLoop {
CFMutableSetRef _commonModes; // Set
CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
CFRunLoopModeRef _currentMode; // Current Runloop Mode
CFMutableSetRef _modes; // Set
...
};
这里有个概念叫“CommonModes”:一个mode可以将自己标记为“Common”属性(通过将其ModeName添加到RunLoop的“commonModes”中)。每当RunLoop的内容发生变化时,RunLoop都会自动将_commonModeItems里的Source/Timer/Observer同步到具有“Common”标记的所有Mode里;
应用场景举例:主线程的RunLoop里有两个预设置的Mode:kCFRunLoopDefaultMode和UITrackingRunLoopMode。这两个Mode都已经被标记为“Common”属性。DefaultMode是APP平时所处的状态,TrackingRunLoopMode是追踪ScrollView滑动时的状态。当你创建一个Timer并加到DefaultMode时,Timer会得到重复回调,但此时滑动一个TableView时,RunLoop会将Mode切换为TrackingRunLoopMode,这时Timer就不会被回调,并且也不会影响到滑动操作;
有时需要一个Timer,在两个Mode钟都能得到回调,一种办法就是将这个Timer分别加入到这两个Mode钟。还有一种方式,就是讲Timer加入到顶层的RunLoop的NSRunLoopCommonModes中。commonModeItems被自动更新到所有具有“Common”属性的Mode里去;
二、RunLoop相关类
1、CFRunLoopModeRef
2、CFRunLoopSourceRef
3、CFRunLoopTimerRef
4、CFRunLoopObserverRef
1、一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer。每次调用RunLoop的主函数时,只能指定其中一个Mode,这个Mode被称作CurrentMode。如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入。这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响;
CFRunLoopMode的结构大致如下:
struct __CFRunLoopMode {
CFStringRef _name; // Mode Name
CFMutableSetRef _sources0; // Set
CFMutableSetRef _sources1; // Set
CFMutableArrayRef _observers; // Array
CFMutableArrayRef _timers; // Array
...
};
系统默认注册的5个Mode
1)kCFRunLoopDefaultMode:主线程在该Mode下运行;
2)UITrackingRunLoopMode:界面跟踪Mode,用于scrollView追踪触摸滑动,保证界面滑动时,不收其它Mode影响;
3)UIInitializationRunLoopMode:刚启动时APP进入的第一个Mode,启动完成后就不再使用;
4)GSEvenReceiveRunLoopMode:接受系统事件的内部Mode,通常情况不用;
5)kCFRunLoopCommonModes:占位用的Mode,不是真正的Mode;
2、CFRunLoopSourceRef是事件源,也称输入员;
官方文档分类:
1)Port-Based Sources 从其他线程或内核发出的;
- Custom Input Sources 自定义的;
- CoCoa Perform Selector Sources
按函数调用栈分类,可分为2类;
Source有两个版本:Source0和Source1.
Source0,非基于Port的,只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用CFRunLoopSourceSigna(source),将这个Source标记为待处理,然后手动调用CFRunLoopWakeUp(runloop)来唤醒RunLoop,让其处理这个事件;
Source1,基于Port的,包含了一个mach_port和一个回调(函数指针),被用于通过内核和其它线程相互发送消息。这种Source能主动唤醒RunLoop的线程;
3、CFRunLoopTimerRef是基于时间的触发器,它和NSTimer是toll-free bridged的,可以混用。其包含一个时间长度和一个回调(函数指针).当其加入到RunLoop时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调;
4、CFRunLoopObserverRef是观察者,每个Observer都包含了一个回调(函数指针),当RunLoop的状态发生变化时,观察者能通过回调接收到这个变化。可以观测的时间点有以下几个:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
kCFRunLoopAllActivities = OxOFFFFFFFU // 活跃中
};
上面的Source/Timer/Observer被统称为mode item,一个item可以被同时加入多个mode。但一个item被重复加入同一个mode时是不会有效果的。如果一个mode中一个item都没有,则RunLoop会直接退出,不进入循环;