Task中Defer块触发"主Actor隔离属性无法从主Actor修改"错误的解决
解决Task defer块中修改主Actor属性的矛盾报错
错误原因
UIViewController默认隶属于主Actor,其成员属性isPerformingAsyncWork因此也被主Actor隔离。但你创建的Task闭包默认不会继承外部的主Actor上下文——哪怕performAsyncWork是在主Actor中调用的,Task闭包仍会在后台Actor执行。defer块会继承Task闭包的执行上下文,因此在defer中修改主Actor隔离的属性时,就会触发隔离检查错误。
那条提示的表述存在歧义,真实含义是:被主Actor隔离的属性,不能在非主Actor的执行上下文中修改,而非“不能从主Actor修改”。
解决方案
根据Task内操作的性质,有两种常用解决方式:
方案1:仅在需要时切换到主Actor(适合含耗时同步操作的场景)
如果Task内包含解码大文件这类耗时同步操作,为避免阻塞主线程,推荐只在修改主Actor属性、操作UI时切换上下文:
class ViewController: UIViewController { var isPerformingAsyncWork = false override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) performAsyncWork() } func performAsyncWork() { guard !isPerformingAsyncWork else { return } let alert = UIAlertController(title: "Working on it…", message: nil, preferredStyle: .alert) let task = Task { // 标记任务开始,切换到主Actor修改属性 await MainActor.run { isPerformingAsyncWork = true } defer { // 任务结束/取消时,切换到主Actor执行收尾 await MainActor.run { isPerformingAsyncWork = false } } let url = URL(string: "https://hws.dev/user-favorites.json")! let (data, _) = try await URLSession.shared.data(from: url) guard !Task.isCancelled else { return } let values = try JSONDecoder().decode([Int].self, from: data) guard !Task.isCancelled else { return } // 更多异步操作、取消检查... // 操作UI必须在主Actor await MainActor.run { alert.presentingViewController?.dismiss(animated: true) } } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in task.cancel() }) present(alert, animated: true) } }
方案2:让整个Task运行在主Actor(适合仅含异步挂起操作的场景)
如果Task内只有异步挂起操作(如URLSession.data(from:)),不会阻塞主线程,可以直接给Task闭包标记@MainActor,让整个任务在主Actor上下文执行:
class ViewController: UIViewController { var isPerformingAsyncWork = false override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) performAsyncWork() } func performAsyncWork() { guard !isPerformingAsyncWork else { return } let alert = UIAlertController(title: "Working on it…", message: nil, preferredStyle: .alert) // 标记Task闭包运行在主Actor let task = Task { @MainActor in defer { isPerformingAsyncWork = false } isPerformingAsyncWork = true let url = URL(string: "https://hws.dev/user-favorites.json")! let (data, _) = try await URLSession.shared.data(from: url) guard !Task.isCancelled else { return } let values = try JSONDecoder().decode([Int].self, from: data) guard !Task.isCancelled else { return } // 更多异步操作、取消检查... alert.presentingViewController?.dismiss(animated: true) } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in task.cancel() }) present(alert, animated: true) } }
补充说明
注意原代码中isPerformingAsyncWork = true这行,如果Task不在主Actor上下文,同样会触发隔离错误,两种方案都已同步修正了这个问题。
内容的提问来源于stack exchange,提问作者Jordan H
相关产品推荐
相关产品推荐

