尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从NSTimer到CFRunLoopTimer:HYBLoopScrollView自动轮播定时器为何两次改造

从NSTimer到CFRunLoopTimer:HYBLoopScrollView自动轮播定时器为何两次改造 从NSTimer到CFRunLoopTimerHYBLoopScrollView自动轮播定时器为何两次改造【免费下载链接】HYBLoopScrollView一行代码接入轮播组件自带图片下载、缓存相关功能无任何第三方依赖、轻量级组件。项目地址: https://gitcode.com/gh_mirrors/hy/HYBLoopScrollViewHYBLoopScrollView 是一款一行代码即可接入的 iOS 轮播图组件自带图片下载、缓存功能无任何第三方依赖。它的核心卖点之一是自动轮播图片每隔几秒自动切换用户手动滑动时自动暂停。这个看似简单的功能背后却藏着 NSTimer 与 RunLoop 的经典坑——组件的定时器经历了两次改造才真正做到“随时能停、随时能启”。自动轮播为什么离不开定时器轮播组件的运行逻辑很简单每隔固定时间比如 5 秒自动切换到下一张图片。一旦用户手指按住屏幕开始滑动就必须立刻暂停定时器否则会出现“手指还没松手图片却自己跳走了”的尴尬体验手指松开后再重新开启。对应的对外 API 就是这两个方法定义在 HYBLoopScrollView.h 中pauseTimer暂停自动轮播通常在视图消失或用户开始滑动时调用startTimer立即启动定时器通常在视图出现或滑动结束时调用内部实现上还有一个关键细节用户拖动的瞬间要停松手后立刻重启。组件在拖动回调中直接调用暂停与启动相关代码位于 HYBLoopScrollView.m- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self pauseTimer]; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { [self startTimer]; }也就是说定时器必须支持高频次、随时性的暂停与恢复——这一点直接决定了后面两次改造的方向。第一次改造NSTimer 为什么“暂停不生效”最初版本使用的是最常见的NSTimer scheduledTimerWithTimeInterval:target:selector:repeats:。它有个著名特性NSTimer 只对所在的 RunLoop 模式生效。scheduledTimer创建的定时器默认注册在NSDefaultRunLoopMode下。问题来了当手指按住轮播图滑动时UIScrollView 会切换到UITrackingRunLoopMode跟踪模式此时默认模式下的 NSTimer完全停止触发——看起来“暂停”成功了。但真正的坑在反过来如果暂停逻辑只是把触发时间改成[NSDate distantFuture]恢复时又依赖旧的 fireDate 技巧就会出现“暂停了但停不住、重启后时快时慢”的问题。这是社区反馈最多的 bugREADME 的版本历史里也记录了 3.1.1 版本之前的多次 timer 修复。第一次改造的思路把定时器加入NSRunLoopCommonModes。这样无论 RunLoop 处于默认模式还是滚动跟踪模式定时器都能正常触发配合拖动开始/结束回调来控制启停。第二次改造迁移到 CFRunLoopTimer彻底解决暂停问题即便加入了 CommonModes围绕 NSTimer 的“改 fireDate 来暂停/恢复”的写法依然不够干净在快速连续调用pauseTimer/startTimer时仍偶现失效。于是组件在3.1.1 版本README 记录“fix timer bug, use core foundation timer add/remove from run loop”做了第二次改造直接改用 Core Foundation 的CFRunLoopTimer思路从“改触发时间”变为“销毁并重建”。现在的实现位于 HYBLoopScrollView.m 的pauseTimer- (void)pauseTimer { if (self.timer) { CFRunLoopTimerInvalidate(self.timer); CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), self.timer, kCFRunLoopCommonModes); } }而startTimer会走configTimer重新创建定时器HYBLoopScrollView.m几个值得新手注意的细节先 Invalidate 再从 RunLoop 移除两步缺一不可只做其中一步都可能造成“幽灵定时器”继续触发。注册在kCFRunLoopCommonModes滚动时也能触发这是 RunLoop Mode 坑的正面解法。回调 Block 中弱引用 self__weak __typeof(self) weakSelf self;避免定时器强持有控件导致的循环引用。这种“用完即销毁、需要时重建”的写法把暂停/启动逻辑简化成了纯粹的“删”与“建”不再依赖任何 fireDate 技巧行为完全可预期。两次改造给新手留下的三个经验⏱️NSTimer 只在注册的 RunLoop 模式下触发凡是涉及滚动、滑动的场景务必加入NSRunLoopCommonModes。暂停定时器的可靠姿势比起“改触发时间”invalidate 移除 重建更简单可靠CFRunLoopTimer 在这方面 API 更直接。回调中永远弱引用 self定时器的 Block 会长期持有引用忘记weakSelf是内存泄漏的经典来源。如何开始使用如果你想亲自验证自动轮播的暂停/恢复效果可以参考 TestViewController.m 中的演示工程或阅读 README.md 中的一行代码接入示例。组件还支持 Cocoapods 安装pod HYBLoopScrollView图片下载与磁盘缓存全部内置收到内存警告时会自动清理缓存。小结一个 5 秒切换一次的轮播定时器背后是 RunLoop Mode、fireDate 技巧、Core Foundation API 三层知识。HYBLoopScrollView 的这两次改造恰好是 iOS 定时器问题的教科书式演进——遇到“定时器不触发”或“暂停不生效”时先想到 RunLoop 模式就不会走弯路了。【免费下载链接】HYBLoopScrollView一行代码接入轮播组件自带图片下载、缓存相关功能无任何第三方依赖、轻量级组件。项目地址: https://gitcode.com/gh_mirrors/hy/HYBLoopScrollView创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表