1 NSRunLoop(线程不安全)和CFRunLoopRef(线程安全)
2 苹果不允许直接创建RunLoop, 它只提供两个自动获取的函数
CFRunLoopGetMain() 和 CFRunLoopGetCurrent()
线程和RunLoop之间是一一对应的, 其关系是保存在一个全局的Dictionary里
线程刚创建时,并没有RunLoop, 如果你不主动获取, 那么它一直都不会有
RunLoop的创建是发生在第一次获取时, RunLoop的销毁发生在线程结束时. 你只能在一个线程内部获取其RunLoop
3 一个RunLoop包含若干Mode, 每个Mode又包含若干个Source/Timer/Observer
每次调用RunLoop的主函数时, 只能指定其中一个Mode, 这个Mode被称为CurrentMode. 如果需要切换Mode, 只能退出Loop,在重新指定一个Mode进入
CFRunLoopSourceRef
Source0 只包含了一个回调
Source1 包含了一个mach_port和一个回调
CFRunLoopTimerRef是基于时间的触发器
CFRunLoopObserverRef是观察者
Source/Timer/Observer被统称为mode item, 一个item可以被同时加入多个mode. 一个item被重复加入同一个mode时是不会有效果的, 如果一个mode中一个item都没有,则RunLoop会直接退出
4
struct __CFRunLoopMode {
CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
CFMutableSetRef _sources0; // Set
CFMutableSetRef _sources1; // Set
CFMutableArrayRef _observers; // Array
CFMutableArrayRef _timers; // Array
...
};
struct __CFRunLoop {
CFMutableSetRef _commonModes; // Set
CFMutableSetRef _commonModeItems; // Set
CFRunLoopModeRef _currentMode; // Current Runloop Mode
CFMutableSetRef _modes; // Set
...
};
CommonModes 一个Mode可以将自己标记为Common属性
每当RunLoop的内容发送变化时, RunLoop都会自动将_commonModeItems里的Source/Observer/Timer同步到具有Common标记的所有Mode里
实例:
主线程RunLoop里有两个预置的Mode: kCFRunLoopDefaultMode和UITrackingRunLoopMode.这两个Mode都已经被标记为Common属性
DefaultMode是app平时所处状态, TrackingRunLoopMode是追踪ScrollView滑动状态.
当你创建一个Timer添加到DefaultMode时,Timer会得到重复回调, 但滑动一个tableview时, RunLoop会将mode切换到TrackingRunLoopMode 这时候Timer就不会被调用
解决方法
1. 将Timer添加到两个Mode钟
2.将Timer加入到commonModeItems, commonModeItems被RunLoop自动更新到所有具有Common属性的Mode里去