ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Swift Promise库进阶技巧:用InvalidatableQueue彻底告别列表复用竞态bug

2026/8/27 16:06:23 拓冰建站 浏览量
Swift Promise库进阶技巧:用InvalidatableQueue彻底告别列表复用竞态bug Swift Promise库进阶技巧用InvalidatableQueue彻底告别列表复用竞态bug【免费下载链接】PromiseA Promise library for Swift, based partially on Javascripts A spec项目地址: https://gitcode.com/gh_mirrors/promi/PromiseSwift Promise库Package.swift 中的Promises包是一个基于 JavaScript A 规范设计的 Promise 异步编程库支持then、catch、all、race等经典用法。今天聚焦一个鲜少被提及却极其实用的进阶能力——InvalidatableQueue可失效执行队列只需几行代码就能彻底告别 UITableView/UICollectionView 列表复用时的竞态 bug再也不会出现第 5 行的图片跑到第 1 行显示的尴尬场面。列表复用中的竞态 bug为什么图片总是张冠李戴想象一个带图片的列表页你快速下拉第 1 行的图片请求发出但还没返回第 1 行的 Cell 被回收复用给新的数据旧请求姗姗来迟把旧图片塞进了新数据对应的 Cell❌问题的本质Promise 的回调在异步执行时并不知道它所关心的 UI 是否还活着。传统的处理办法是手动加isCellVisible之类的标志位判断繁琐且容易漏。而 Promise 库给出了优雅的解法。InvalidatableQueue 是什么一个可以一键作废的执行队列Promise 库中每个接受闭包的方法如then、catch都接受一个ExecutionContext执行上下文参数通常传的是DispatchQueuefunc then(on queue: ExecutionContext DispatchQueue.main, ...)关键在于ExecutionContext是一个协议任何实现它类型的对象都能传入。InvalidatableQueue就是其中之一实现仅 20 行代码public final class InvalidatableQueue: ExecutionContext { private var valid true private var queue: DispatchQueue public init(queue: DispatchQueue .main) { self.queue queue } public func invalidate() { valid false } public func execute(_ work: escaping () - Void) { guard valid else { return } self.queue.async(execute: work) } }来源Promise/Promise.swift#L25-L44逻辑一目了然调用invalidate()之后所有后续投递到该队列的任务都会被静默丢弃根本不会执行。三步上手在 TableView 单元格中应用 InvalidatableQueue第 1 步让每个 Cell 持有自己的 InvalidatableQueueclass SomeTableViewCell: UITableViewCell { var invalidatableQueue InvalidatableQueue() }第 2 步把 Promise 的回调指定到这个队列上执行func showImage(at url: URL) { ImageFetcher(url).fetch() .then(on: invalidatableQueue) { image in self.imageView.image image // 只有 Cell 还有效才会执行 } }第 3 步在prepareForReuse()中失效并重置队列override func prepareForReuse() { super.prepareForReuse() invalidatableQueue.invalidate() invalidatableQueue InvalidatableQueue() }这样任何在 Cell 被复用后返回的旧请求其回调都会因为队列已失效而被自动丢弃——竞态 bug 从根上消失✅两个进阶技巧避开链式中断陷阱技巧 1失效队列上只跑终点站闭包官方 README 中有明确警告见README.md#L247不要在 InvalidatableQueue 执行的闭包上继续链式返回新值或新 Promise。返回Void的闭包是安全的但返回值的then闭包会中断整条 Promise 链——因为闭包无法执行后续 Promise 将永远停留在pending状态导致资源无法释放。 最佳实践把then(on: invalidatableQueue, ...)放在链的最后一环。技巧 2支持非主队列性能更灵活InvalidatableQueue(queue:)允许传入任意队列不必局限于主线程let backgroundQueue DispatchQueue(label: cell.ui.queue) let invalidatableQueue InvalidatableQueue(queue: backgroundQueue)项目的测试用例PromiseTests/ExecutionContextTests.swift#L53-L132覆盖了全部核心场景未失效队列正常执行、失效后闭包被丢弃、失效不阻塞后续主队列回调、以及非主队列支持。可以直接参考这些测试为自己的代码写单测。顺带一提Promise 库的线程模型理解线程模型能帮你少踩坑init(work:)默认在后台全局队列执行而then、catch、always等所有块式方法默认回到主线程执行。你可以随时通过on:参数覆盖Promise(work: { fulfill, reject in ... }) .then(on: DispatchQueue.global()) { ... } // 后台 .then(on: DispatchQueue.main) { ... } // 主线程配合 InvalidatableQueue你就获得了一套完整的异步 UI 更新方案任务在后台跑结果在受控的队列上落地过时的结果自动作废。小结为什么值得收藏这个技巧场景传统做法InvalidatableQueue 做法判断 Cell 是否仍有效手写标志位 各种判断队列失效后自动丢弃 ✅代码量每个异步点都要判断仅prepareForReuse中 2 行出错概率容易漏判、埋下隐患机制化保证几乎零遗漏InvalidatableQueue 的价值不在于代码量小确实只有 20 行而在于它把异步结果是否还有效这个业务判断下沉成了基础设施——这正是 Promise 库让异步组合变得系统化这一设计哲学的绝佳注脚。 相关源码位置速查核心实现Promise/Promise.swiftInvalidatableQueue与ExecutionContext协议扩展方法Promise/PromiseExamples.swift之外的完整扩展在Promise/PromiseExtras.swift测试参考PromiseTests/ExecutionContextTests.swift安装CocoaPods 添加pod Promises后执行pod install即可见Promises.podspec【免费下载链接】PromiseA Promise library for Swift, based partially on Javascripts A spec项目地址: https://gitcode.com/gh_mirrors/promi/Promise创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考