如何避免async函数持有self引用且无需显式取消Task?
问题解决:异步方法执行时避免强引用
self的两种简洁方案 你的代码中,fetchMovies作为实例异步方法,执行期间会强引用self,导致TestViewModel实例无法在任务完成前被销毁(deinit不会立即触发)。结合你不想显式管理Task、不想合并代码的需求,以下是两个直接的解决办法:
方案一:静态方法+弱引用参数
将fetchMovies改为静态方法,传入weak self作为参数。静态方法不会强持有self,仅在需要访问实例成员时检查弱引用有效性,任务执行期间不会阻碍实例销毁:
class TestViewModel { init() { print("Init") Task { [weak self] in await Self.fetchMovies(for: self) } } deinit { print("Deinit") } static func fetchMovies(for weakSelf: TestViewModel?) async { try? await Task.sleep(nanoseconds: 3 * 1_000_000_000) // 仅当self仍存在时,执行依赖实例的后续操作 guard let self = weakSelf else { return } print("后续实例操作:\(self)") } }
这个方案逻辑拆分清晰,无需管理Task,也不会强持self。当TestViewModel实例被释放时,weakSelf会变为nil,后续依赖实例的代码自动跳过,实例可以正常执行deinit。
方案二:隐式绑定Task到实例,deinit自动取消
通过关联对象将Task隐式绑定到self,在deinit时自动取消任务。无需显式声明Task属性,任务会随实例销毁而终止,打破self的强引用:
import ObjectiveC private var AssociatedTaskKey: UInt8 = 0 class TestViewModel { init() { print("Init") let task = Task { [weak self] in await self?.fetchMovies() } // 用关联对象隐式存储Task objc_setAssociatedObject(self, &AssociatedTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } deinit { print("Deinit") // 实例销毁时自动取消Task if let task = objc_getAssociatedObject(self, &AssociatedTaskKey) as? Task<Void> { task.cancel() } } func fetchMovies() async { do { try await Task.sleep(nanoseconds: 3 * 1_000_000_000) print("任务完成") } catch is CancellationError { print("任务随实例销毁被取消") } catch { // 处理其他错误 } } }
这个方案保留了fetchMovies的实例方法结构,Task的管理完全隐式。当实例销毁时,Task被自动取消,Task.sleep会抛出CancellationError,任务提前终止,self的强引用被释放。
内容的提问来源于stack exchange,提问作者El Horrible
相关产品推荐
相关产品推荐

