Swift中在主线程通过async/await获取结果的其他方法
在后台任务更新UI的警告问题及解决方案
你遇到的问题是在异步方法中更新UI时触发后台操作UI的警告,原代码如下:
func didInit() async { listOfTodo = await interactor.getTodos() }
尝试用DispatchQueue.main.async {}包裹时出现错误,后续通过给函数添加@MainActor解决了问题,下面是其他可行的方案及解析:
可行方案
1. 用MainActor.run包裹UI更新代码
如果只是更新UI的部分需要切换到主线程,不需要给整个函数标记@MainActor,可以将赋值操作放在MainActor.run中,注意要加上await:
func didInit() async { // 在后台获取数据 let todos = await interactor.getTodos() // 切换到主线程更新UI await MainActor.run { listOfTodo = todos } }
这种方式更灵活,适合函数中只有部分逻辑需要主线程的场景。
2. 给UI属性的setter标记@MainActor
如果listOfTodo是UI相关属性,本身就应该在主线程更新,可以直接给它的setter添加@MainActor,这样无论在哪里赋值,都会自动切换到主线程:
@MainActor var listOfTodo: [Todo] = [] func didInit() async { listOfTodo = await interactor.getTodos() }
这种方式更贴合UI属性的设计原则,从根源上保证属性的线程安全。
3. 给整个函数标记@MainActor(你找到的方案)
如果整个函数的逻辑都需要在主线程执行(比如除了赋值还有其他UI操作),直接给函数加@MainActor是最简洁的方式:
@MainActor func didInit() async { listOfTodo = await interactor.getTodos() }
为什么DispatchQueue.main.async {}会报错?
在异步函数中直接使用同步的DispatchQueue.main.async会导致错误,因为异步函数需要遵循结构化并发规则。如果非要用GCD,需要将其包装成异步任务,但这种写法冗余,不推荐:
func didInit() async { let todos = await interactor.getTodos() await withCheckedContinuation { continuation in DispatchQueue.main.async { self.listOfTodo = todos continuation.resume() } } }
方案选择建议
- 仅部分代码需要主线程:选
MainActor.run - UI属性本身需主线程更新:给属性setter加
@MainActor - 整个函数逻辑都在主线程:给函数加
@MainActor
内容的提问来源于stack exchange,提问作者Steven-Carrot
相关产品推荐
相关产品推荐

