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

Swift中如何让旧协议适配MainActor隔离?

解决QLPreviewPanel协议与MainActor并发警告的方案

针对QLPreviewPanelDataSource协议方法因主Actor隔离导致的并发警告,在保持Strict Concurrency Checking为complete的前提下,有以下几种可靠的解决方式:

方案一:用nonisolated(unsafe)标记协议方法

由于QuickLook预览面板的相关方法始终在主线程调用(系统固定行为),我们可以安全使用nonisolated(unsafe)标记协议实现方法,绕过编译器的隔离检查。这既符合系统实际调用逻辑,又能消除警告。

代码示例:

@MainActor
final class SomeController: NSTableViewDataSource, QLPreviewPanelDataSource
{
    private var tableView: NSTableView

    nonisolated(unsafe) func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int {
       return tableView.numberOfSelectedRows
    }
}

注:此处的unsafe仅为编译器层面的标记,实际因调用线程固定为主线程,不存在并发安全风险。

方案二:缓存选中行数到非隔离属性

通过在主线程更新一个非隔离的选中行数缓存,让协议方法直接读取缓存值,避免访问主Actor隔离的tableView。

代码示例:

@MainActor
final class SomeController: NSTableViewDataSource, QLPreviewPanelDataSource, NSTableViewDelegate
{
    private var tableView: NSTableView
    private nonisolated var cachedSelectedRowCount = 0

    init(tableView: NSTableView) {
        self.tableView = tableView
        self.tableView.delegate = self
        cachedSelectedRowCount = tableView.numberOfSelectedRows
    }

    func tableViewSelectionDidChange(_ notification: Notification) {
        cachedSelectedRowCount = tableView.numberOfSelectedRows
    }

    nonisolated func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int {
       return cachedSelectedRowCount
    }
}

该方案更贴合Swift并发设计原则,通过缓存避免跨Actor直接访问,适合对unsafe标记有顾虑的场景。

方案三:创建非隔离的代理对象

将QLPreviewPanelDataSource的实现拆分到独立的非隔离代理类中,代理类通过主Actor控制器实例获取数据,利用MainActor.run确保在主线程访问UI资源。

代码示例:

final class PreviewPanelDataSource: NSObject, QLPreviewPanelDataSource {
    private weak var controller: SomeController?

    init(controller: SomeController) {
        self.controller = controller
    }

    func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int {
        return MainActor.run { [weak self] in
            self?.controller?.tableView.numberOfSelectedRows ?? 0
        }
    }
}

@MainActor
final class SomeController: NSTableViewDataSource {
    private var tableView: NSTableView
    private lazy var previewDataSource = PreviewPanelDataSource(controller: self)

    init(tableView: NSTableView) {
        self.tableView = tableView
        QLPreviewPanel.shared()?.dataSource = previewDataSource
    }
}

此方案将协议实现与主Actor控制器分离,适合需要更清晰代码结构的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 07:42:35