Swift中不使用闭包在UI线程执行代码的替代方案
不使用闭包在UI线程执行代码的Swift实现
在后台线程更新UI时,必须通过DispatchQueue.main.async(execute:)将任务提交到主线程,常规闭包实现如下:
static func executeInUIThread(_ uiThreadFunc: @escaping (Any?) -> Void, _ parms: Any?) { DispatchQueue.main.async { // 更新UI uiThreadFunc(parms) } }
闭包可捕获上下文变量,因此能直接访问uiThreadFunc和parms。但如果不想使用Swift的闭包(Lambda风格)编程,尝试的无闭包写法无法传递参数,直接用静态变量存储又会引发多线程安全问题,以下是两种可行的解决方案:
方案1:借助Objective-C的performSelector API
Swift兼容Objective-C的消息传递机制,可通过NSObject的performSelector(onMainThread:with:waitUntilDone:)实现无闭包的主线程任务提交:
static func executeInUIThread(_ uiThreadFunc: @escaping (Any?) -> Void, _ parms: Any?) { // 封装函数和参数为元组,转为AnyObject类型传递 let taskTuple = (uiThreadFunc, parms) as AnyObject self.performSelector( onMainThread: #selector(internalExecuteTask), with: taskTuple, waitUntilDone: false ) } @objc private static func internalExecuteTask(_ args: Any) { guard let (task, params) = args as? ((Any?) -> Void, Any?) else { return } task(params) }
方案2:自定义载体类+关联对象(完全无闭包)
通过自定义类承载任务和参数,结合Objective-C的关联对象机制绑定到DispatchWorkItem,在内部方法中取出执行:
// 关联对象的Key定义 private enum AssociatedKeys { static var taskHolder = "taskHolder" } // 承载任务与参数的类 private class TaskCarrier { let targetTask: (Any?) -> Void let taskParams: Any? init(task: @escaping (Any?) -> Void, params: Any?) { self.targetTask = task self.taskParams = params } } static func executeInUIThread(_ uiThreadFunc: @escaping (Any?) -> Void, _ parms: Any?) { let carrier = TaskCarrier(task: uiThreadFunc, params: parms) let workItem = DispatchWorkItem(block: InternalExecuteInUIThread) // 将载体对象绑定到workItem objc_setAssociatedObject( workItem, &AssociatedKeys.taskHolder, carrier, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) DispatchQueue.main.async(execute: workItem) } private static func InternalExecuteInUIThread() { // 获取当前执行的workItem,取出绑定的载体对象 guard let currentWorkItem = Thread.current.currentQueue?.currentWorkItem(), let carrier = objc_getAssociatedObject(currentWorkItem, &AssociatedKeys.taskHolder) as? TaskCarrier else { return } carrier.targetTask(carrier.taskParams) }
方案对比
- 方案1实现简单,依赖Objective-C桥接,代码量少,适合大多数场景。
- 方案2完全摆脱闭包写法,通过关联对象传递参数,逻辑稍复杂但更贴合“无闭包”的严格要求。
内容的提问来源于stack exchange,提问作者NightFuryLxD
相关产品推荐
相关产品推荐

