利用RunLoop空闲时间执行预缓存任务
利用RunLoop空闲时间执行预缓存任务
最近在做高度自适应的UITableView的时候,使用了一个FDTemplateLayoutCell的开源组件。
它的主要原理是利用RunLoop空闲时间执行预缓存任务。
FDTemplateLayoutCell 的高度预缓存是一个优化功能,它要求页面处于空闲状态时才执行计算,当用户正在滑动列表时显然不应该执行计算任务影响滑动体验。一般来说,这个功能要耦合 UITableView 的滑动状态才行,但这种实现十分不优雅且可能破坏外部的 delegate 结构,但好在我们还有RunLoop
这个工具,了解它的运行机制后,可以用很简单的代码实现上面的功能。
空闲RunLoopMode
当用户正在滑动 UIScrollView 时,RunLoop 将切换到 UITrackingRunLoopMode
接受滑动手势和处理滑动事件(包括减速和弹簧效果),此时,其他 Mode (除 NSRunLoopCommonModes 这个组合 Mode)下的事件将全部暂停执行,来保证滑动事件的优先处理,这也是 iOS 滑动顺畅的重要原因。当 UI 没在滑动时,默认的 Mode 是 NSDefaultRunLoopMode
(同 CF 中的 kCFRunLoopDefaultMode),同时也是 CF 中定义的 “空闲状态 Mode”。当用户啥也不点,此时也没有什么网络 IO 时,就是在这个 Mode 下。
用RunLoopObserver找准时机
注册 RunLoopObserver 可以观测当前 RunLoop 的运行状态,并在状态机切换时收到通知:
RunLoop开始
RunLoop即将处理Timer
RunLoop即将处理Source
RunLoop即将进入休眠状态
RunLoop即将从休眠状态被事件唤醒
RunLoop退出
因为“预缓存高度”的任务需要在最无感知的时刻进行,所以应该同时满足:
- RunLoop 处于“空闲”状态 Mode
当这一次 RunLoop 迭代处理完成了所有事件,马上要休眠时
使用 CF 的带 block 版本的注册函数可以让代码更简洁:CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFStringRef runLoopMode = kCFRunLoopDefaultMode; CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) { // TODO here}); CFRunLoopAddObserver(runLoop, observer, runLoopMode);
在其中的 TODO 位置,就可以开始任务的收集和分发了,当然,不能忘记适时的移除这个 observer。
分解成多个RunLoop Source任务
假设列表有 20 个 cell,加载后展示了前 5 个,那么开启估算后 table view 只计算了这 5 个的高度,此时剩下 15 个就是“预缓存”的任务,而我们并不希望这 15 个计算任务在同一个 RunLoop 迭代中同步执行,这样会卡顿 UI,所以应该把它们分别分解到 15 个 RunLoop 迭代中执行,这时就需要手动向 RunLoop 中添加 Source 任务(由应用发起和处理的是 Source 0 任务)Foundation 层没对 RunLoopSource 提供直接构建的 API,但是提供了一个间接的、既熟悉又陌生的 API:
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
这个方法将创建一个 Source 0 任务,分发到指定线程的 RunLoop 中,在给定的 Mode 下执行,若指定的 RunLoop 处于休眠状态,则唤醒它处理事件,简单来说就是“睡你xx,起来嗨!”于是,我们用一个可变数组装载当前所有需要“预缓存”的 index path,每个 RunLoopObserver 回调时都把第一个任务拿出来分发:
NSMutableArray *mutableIndexPathsToBePrecached = self.fd_allIndexPathsToBePrecached.mutableCopy; CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _) {if (mutableIndexPathsToBePrecached.count == 0) { CFRunLoopRemoveObserver(runLoop, observer, runLoopMode); CFRelease(observer); // 注意释放,否则会造成内存泄露 return; } NSIndexPath *indexPath = mutableIndexPathsToBePrecached.firstObject; [mutableIndexPathsToBePrecached removeObject:indexPath]; [self performSelector:@selector(fd_precacheIndexPathIfNeeded:) onThread:[NSThread mainThread] withObject:indexPath waitUntilDone:NO modes:@[NSDefaultRunLoopMode]]; });
这样,每个任务都被分配到下个“空闲” RunLoop 迭代中执行,其间但凡有滑动事件开始,Mode 切换成 UITrackingRunLoopMode,所有的“预缓存”任务的分发和执行都会自动暂定,最大程度保证滑动流畅。
利用RunLoop空闲时间执行预缓存任务相关推荐
- 下班做什么副业?利用下班空闲时间赚点外快!
下班做什么副业?利用下班空闲时间赚点外快! 大城市生活压力巨大,房贷.伙食.生活费扣除后一个月工资所剩无几,但到手的工资却不见增长.有些上班族的工作比较清闲,因此下班后的时间就可以利用起来,赚点小钱. ...
- 学计算机空闲时间,空闲时间
在计算机中,CPU的运行速度远远快于I/O设备的速度,当内存中仅有一道程序时,每逢该程序在运行中发出I/O请求后,CPU空闲,CPU等待I/O设备完成I/O请求的时间,称为CPU空闲时间.操作系统采用 ...
- 怎么样利用空闲时间做网赚?
今天这期内容大周来给粉丝们分享一个适合新手操作的方法,不需要露脸拍摄视频,在家用手机也可以操作起来. 不用担心自己没有颜值.没有技术.没有才艺,用手机做视频剪辑,每天稳定收益200-300还是可以的. ...
- 利用空闲时间挣钱的兼职
空闲时间的兼职 体力类 跑腿类 调查类 推广类 陪玩类 配音类.直播类 小时工 代购类 电商类 休闲类.听音乐.看视频 技能类 驾驶类 PPT 写作类 拆书稿 翻译类 技能平台类 程序技术类 设计类 ...
- 程序员如何利用空闲时间挣零花钱
一: 私活 作为一名程序员,在上班之余,我们有大把的时间,不能浪费,这些时间其实都是可以用来挖掘自己潜在的创造力,今天要讨论的话题就是,程序员如何利用空余时间挣零花钱?比如说周末可以赚外快啊,在网上接 ...
- iOS 之如何利用 RunLoop 原理去监控卡顿?
[CSDN 编者按]简单来说APP卡顿,就是FPS达不到60帧率,丢帧现象,就会卡顿,但是很多时候,我们只知道丢帧了,具体为什么丢帧,却不是很清楚,那么我们要怎么监控呢? 作者 | 枫叶无处漂泊 ...
- 如何利用 RunLoop 原理去监控卡顿?
卡顿问题,就是在主线程上无法响应用户交互的问题.如果一个 App 时不时地就给你卡一下,有时还长时间无响应,这时你还愿意继续用它吗?所以说,卡顿问题对 App 的伤害是巨大的,也是我们必须要重点解决的 ...
- SQL Server 执行计划缓存
原文:SQL Server 执行计划缓存 标签:SQL SERVER/MSSQL SERVER/数据库/DBA/内存池/缓冲区 概述 了解执行计划对数据库性能分析很重要,其中涉及到了语句性能分析与存储 ...
- java dataset redis,利用Spring-Data-Redis和Jedis操作Redis缓存
概述 Jedis是redis官方推荐的用于访问Java客户端,在https://github.com/xetorthio/jedis下载最新的jedis. 访问redis 1.访问简 ...
最新文章
- JavaScript继承详解(四)
- 人参中第一次膜你退货
- iPhone4S出现应用无法打开时的解决方案
- 如何在UIAlertView中显示进度条
- live messenger与稀疏文件—Sparse File Bit
- java中channelmessage,MessageStore支持的QueueChannel与Spring Integration Java Config
- 20220209-CTF MISC-BUUCTF-伪加密(ZIP文件块 十六进制分析)
- 洛谷 P1091 合唱队型
- ISCC2014-reverse
- 【HDU2825】Wireless Password【AC自动机,状态压缩DP】
- 如何在旅途中提升 MacBook 电池电量?
- 厉害,96秒100亿,阿里双十一到底做了什么杠过亿级流量??
- Bregman 散度
- 数字图像处理实践(二)
- 100台电脑无盘服务器配置,100台网吧无盘系统配三星840PRO方案解读
- python 爬取网易云音乐歌单
- UNIX编程艺术学习笔记-1
- linux ftp命令大全,linux ftp命令详解
- Docker容器引擎
- 一个简单的猜数字游戏(附带关机惩罚)