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
相关产品推荐
相关产品推荐

