iOS CFRUnLoopRef 之CFRunLoopMode
来源:互联网 发布:网络制式按什么划分 编辑:程序博客网 时间:2024/06/08 05:55
1.简介:
每个CFRunLoopRef 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。
CFRunLoopModeRef
类并没有对外暴露,只是通过 CFRunLoopRef 的接口进行了封装。他们的关系如下:
CFRunLoopRef获取Mode的接口:
CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);CFRunLoopRunInMode(CFStringRef modeName, ...);
2.CFRunLoopMode的类型
kCFRunLoopDefaultMode
App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode:
界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
- UIInitializationRunLoopMode:
在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
- GSEventReceiveRunLoopMode:
接受系统事件的内部 Mode,通常用不到
- kCFRunLoopCommonModes:
这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode
3.CFRunLoopMode 和 CFRunLoop 的结构
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 ...};
4. CommonModes(很实际问题)
- 一个 Mode 可以将自己标记为”Common”属性(通过将其 ModeName 添加到 RunLoop 的 “commonModes” 中)。RunLoop 会自动将 _commonModeItems 加入到具有 “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 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。
- 有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。
5.Mode之间的切换
理解了CommonModes,那么下面的只能算是具体应用,以便加深理解
我们平时在开发中一定遇到过,当我们使用NSTimer每一段时间执行一些事情时滑动UIScrollView,NSTimer就会暂停,当我们停止滑动以后,NSTimer又会重新恢复的情况,我们通过一段代码来看一下:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ // [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES]; NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES]; // 加入到RunLoop中才可以运行 // 1. 把定时器添加到RunLoop中,并且选择默认运行模式NSDefaultRunLoopMode = kCFRunLoopDefaultMode // [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; // 当textFiled滑动的时候,timer失效,停止滑动时,timer恢复 // 原因:当textFiled滑动的时候,RunLoop的Mode会自动切换成UITrackingRunLoopMode模式,因此timer失效,当停止滑动,RunLoop又会切换回NSDefaultRunLoopMode模式,因此timer又会重新启动了 // 2. 当我们将timer添加到UITrackingRunLoopMode模式中,此时只有我们在滑动textField时timer才会运行 // [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; // 3. 那个如何让timer在两个模式下都可以运行呢? // 3.1 在两个模式下都添加timer 是可以的,但是timer添加了两次,并不是同一个timer // 3.2 使用站位的运行模式 NSRunLoopCommonModes标记,凡是被打上NSRunLoopCommonModes标记的都可以运行,下面两种模式被打上标签 //0 : <CFString 0x10b7fe210 [0x10a8c7a40]>{contents = "UITrackingRunLoopMode"} //2 : <CFString 0x10a8e85e0 [0x10a8c7a40]>{contents = "kCFRunLoopDefaultMode"} // 因此也就是说如果我们使用NSRunLoopCommonModes,timer可以在UITrackingRunLoopMode,kCFRunLoopDefaultMode两种模式下运行 [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; NSLog(@"%@",[NSRunLoop mainRunLoop]);}-(void)show{ NSLog(@"-------");}
由上述代码可以看出,NSTimer不管用是因为Mode的切换,因为如果我们在主线程使用定时器,此时RunLoop的Mode为kCFRunLoopDefaultMode,即定时器属于kCFRunLoopDefaultMode,那么此时我们滑动ScrollView时,RunLoop的Mode会切换到UITrackingRunLoopMode,因此在主线程的定时器就不在管用了,调用的方法也就不再执行了,当我们停止滑动时,RunLoop的Mode切换回kCFRunLoopDefaultMode,所有NSTimer就又管用了。
同样道理的还有ImageView的显示,如下:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ NSLog(@"%s",__func__); // performSelector默认是在default模式下运行,因此在滑动ScrollView时,图片不会加载 // [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"abc"] afterDelay:2.0 ]; // inModes: 传入Mode数组 [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"abc"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode,UITrackingRunLoopMode]];
6. CFRunLoopMode 的item
Mode中的 Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。
Mode 暴露的管理 mode item 的接口有下面几个:
CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode)
- 你只能通过 mode name 来操作内部的 mode,当你传入一个新的 mode name 但 RunLoop 内部没有对应 mode 时,RunLoop会自动帮你创建对应的 CFRunLoopModeRef。对于一个 RunLoop 来说,其内部的 mode 只能增加不能删除。
- 苹果公开提供的 Mode 有两个:kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 和 UITrackingRunLoopMode,你可以用这两个 Mode Name 来操作其对应的 Mode。
- 同时苹果还提供了一个操作 Common 标记的字符串:kCFRunLoopCommonModes (NSRunLoopCommonModes),你可以用这个字符串来操作 Common Items,或标记一个 Mode 为 “Common”。使用时注意区分这个字符串和其他 mode name。
- iOS CFRUnLoopRef 之CFRunLoopMode
- 8、CFRunLoopMode
- RunLoop学习笔记,从CF层面了解由于CFRunLoopMode机制iOS程序ScrollView的滑动为何如此平滑的原因。还有介绍AFNetworking如何单独发起一个global threa
- CFRunLoopRef 的内部逻辑(向 ibireme学习)
- RunLoop学习笔记,从CF层面了解由于CFRunLoopMode机制
- iOS 之@()
- [iOS]深入浅出 iOS 之生命周期
- [iOS]深入浅出 iOS 之生命周期
- IOS深入浅出 iOS 之生命周期
- IOS之启程IOS简介
- IOS开发之IOS生命周期
- 【iOS】iOS之viewcontroller详解
- IOS 笔记之 ios & javascript
- iOS-------iOS编程之NSUserDefault
- 【IOS】IOS OpenCV之HelloWorld
- iOS之iOS键盘隐藏
- iOS之iOS键盘隐藏
- ofstream之ios::ate,ios::app,ios::in,ios::out
- 猫哥带你去战斗——Java Web开发——Java篇[0]——小谈动态网页
- 交换与路由练习(二、排错)
- 蓝桥杯-特殊的数字
- 洛谷P1803 凌乱的yyy
- sizeof和strlen的区别
- iOS CFRUnLoopRef 之CFRunLoopMode
- STM32 --UART串口通信
- 2.1.1
- 猫哥带你去战斗——Java Web开发——Java篇[1]——从内存讲起
- hdu2896(病毒侵袭)--AC自动机
- 【t050】方程求解
- 如何从 Hibernate官网下载 hibernate原码 和hibernatetools
- CFRunLoopRef 的内部逻辑(向 ibireme学习)
- 17/2/1 学习日记