传递不可发送类型的参数“(任何 URLSessionTaskDelegate)?”在主要参与者隔离的上下文之外可能会引入数据竞争

问题描述 投票:0回答:1

我正在使用 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

但是警告仍然存在

ios swift swiftui concurrency swift-concurrency
1个回答
0
投票

警告谈论的是

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 { ... }
。当视图消失时,这些修改器会自动取消任务。

© www.soinside.com 2019 - 2024. All rights reserved.