我正在使用 SwiftUI,并将 View 声明为 @MainActor 来解决一些并发警告。但是,我仍然收到警告
Passing argument of non-sendable type '(any URLSessionTaskDelegate)?' outside of main actor-isolated context may introduce data races
开
let (data, _) = try await URLSession.shared.data(from: svgURL)
我根本没有通过任何
URLSessionTaskDelegate
。
我尝试查看 URLSession.shared.data 的文档并 git this
/// Convenience method to load data using a URL, creates and resumes a URLSessionDataTask internally.
///
/// - Parameter url: The URL for which to load data.
/// - Parameter delegate: Task-specific delegate.
/// - Returns: Data and response.
public func data(from url: URL, delegate: (any URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse)
我不明白,我尝试传递 nil 但错误仍然没有消失。我做错了什么?
有关更多上下文,这里有一个简化的代码
@MainActor
struct EntryIcon: View {
private func loadImage() {
....
if let svgURL = URL(string: urlString) {
Task {
do {
// WARNING: Passing argument of non-sendable type '(any URLSessionTaskDelegate)?' outside of main actor-isolated context may introduce data races
let (data, _) = try await URLSession.shared.data(from: svgURL)
let uiImage = SVG(data: data)?.rasterize().withRenderingMode(.alwaysTemplate).withTintColor(UIColor(color))
if let uiImage {
image = Image(uiImage: uiImage)
}
} catch {
logger.error("error \(error)")
}
}
}
...
}
}
我也尝试过放
Task { @MainActor in
但是警告仍然存在
警告谈论的是
delegate
的
data(from:delegate:)
参数。该参数有一个默认值 (nil
),因此即使您没有显式传递任何内容,它仍然会被传递。
Task
在主要参与者隔离的上下文中运行,但data(from:delegate:)
并未与主要参与者隔离,因此您将委托从主要参与者隔离的上下文传递到非隔离的上下文。正如警告所说,“在主要演员隔离的背景之外”。
因此,您应该首先从非隔离上下文中调用
data(from:delegate:)
。一个简单的方法是创建一个 data(from:delegate)
的包装器,它没有 delegate
参数。
extension URLSession {
func data(from url: URL) async throws -> (Data, URLResponse) {
// this call is in a non-isolated context, so all is good :)
try await URLSession.shared.data(from: url, delegate: nil)
}
}
之后,您的
Task { ... }
中的调用将自动解析为上面的过载。您仍然从主要参与者隔离的上下文中调用非隔离函数,但这一次,您不再传递 URLSessionTaskDelegate
(并且 URL
是 Sendable
),因此不会出现警告。
旁注:在
View
中,请考虑使用 task
和 task(id:)
修饰符,而不是创建顶级 Task { ... }
。当视图消失时,这些修改器会自动取消任务。