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

如何在Actor内部监听OperationQueue的operationCount并对外发布变更?

解决方案

你遇到的警告本质是Actor同步初始化过程中,不允许将self捕获到逃逸闭包中——此时Actor尚未完成完整初始化,闭包的执行也不在Actor的隔离域内,存在线程安全风险。这里推荐两种实现方案:

方案1:直接暴露KVO Publisher(最简最优)

OperationQueue作为NSObject子类,其支持KVO的属性可以直接生成Combine Publisher,不需要手动在闭包中捕获self,完全避开初始化捕获的问题:

import Combine

actor MyActor {
    let theQueue = OperationQueue()
    // 非隔离公共Publisher,外部可直接订阅接收operationCount变更
    nonisolated let operationCountPublisher: AnyPublisher<Int, Never>
    private var cancellables = Set<AnyCancellable>()

    init() {
        // 初始化阶段直接生成Publisher,无需捕获self
        operationCountPublisher = theQueue
            .publisher(for: \.operationCount, options: [.new])
            .eraseToAnyPublisher()
    }

    // 若需要Actor内部处理变更,可在初始化完成后调用该方法
    func setupInternalObservation() {
        operationCountPublisher
            .sink { [weak self] count in
                guard let self else { return }
                // 此处已在Actor隔离域,可安全调用Actor的属性和方法
                print("OperationCount更新:\(count)")
                // 按需触发其他内部业务逻辑即可
            }
            .store(in: &cancellables)
    }
}

外部订阅使用方式示例:

let myActor = MyActor()
myActor.operationCountPublisher
    .receive(on: DispatchQueue.main)
    .sink { count in
        // 业务侧处理变更逻辑
        print("当前队列任务数:\(count)")
    }
    .store(in: &cancellables)

// 若需要内部处理,初始化完成后调用即可
Task {
    await myActor.setupInternalObservation()
}

该方案优势:

  • 完全规避初始化阶段捕获self的安全问题
  • 对外暴露的Publisher是非隔离属性,外部订阅不需要加await,使用成本低
  • 不依赖异步初始化,兼容现有同步初始化的调用逻辑

方案2:使用异步初始化器

如果你需要保留手动KVO监听的写法,可以把MyActor的初始化器声明为异步,Swift允许异步初始化器中把self捕获到逃逸闭包:

import Combine

actor MyActor {
    let theQueue = OperationQueue()
    private var kvoObservation: NSKeyValueObservation?
    // 也可以自定义PassthroughSubject对外发布事件
    private let countSubject = PassthroughSubject<Int, Never>()
    nonisolated let operationCountPublisher: AnyPublisher<Int, Never>

    init() async {
        operationCountPublisher = countSubject.eraseToAnyPublisher()
        // 异步初始化器内可安全捕获self
        kvoObservation = theQueue.observe(\.operationCount, options: .new) { [weak self] _, change in
            guard let self, let newCount = change.newValue else { return }
            Task {
                await self.countSubject.send(newCount)
            }
        }
    }
}

使用时需要用await初始化实例:

Task {
    let myActor = await MyActor()
    myActor.operationCountPublisher
        .receive(on: DispatchQueue.main)
        .sink { count in
            print("任务数更新:\(count)")
        }
        .store(in: &cancellables)
}

内容的提问来源于stack exchange,提问作者zumzum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 13:06:06