You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 23:27:19