如何解决‘非主Actor上下文传递NSItemProvider引发数据竞争’警告?
解决NSItemProvider非Sendable类型的并发警告
这个警告是因为NSItemProvider不属于Sendable类型,在非主Actor隔离的上下文里调用它的方法时,Swift并发运行时无法保证线程安全,可能引发数据竞争。以下是几种可行的解决方式:
方法1:将处理函数标记为主Actor隔离
NSItemProvider通常和UI操作绑定,直接把整个处理函数放到主Actor上下文,确保所有对它的访问都在主线程执行:
@MainActor private func handleIncomingURL(itemProvider: NSItemProvider) async -> String? { do { // 推荐用Swift原生URL代替NSURL,更符合现代风格且是Sendable类型 if let url = try await itemProvider.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) as? URL { if let urlString = url.absoluteString?.trimmingCharacters(in: .whitespacesAndNewlines), ShareViewController.isValidWebsiteURL(urlString) { return urlString } } } catch { print("Error handling incoming URL: \(error)") } return nil } @MainActor private func handleIncomingText(itemProvider: NSItemProvider) async -> String? { do { let text = try await itemProvider.loadItem(forTypeIdentifier: UTType.text.identifier, options: nil) as? String if let text = text?.trimmingCharacters(in: .whitespacesAndNewlines) { do { let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) let matches = detector.matches( in: text, options: [], range: NSRange(location: 0, length: text.utf16.count) ) for match in matches { if let range = Range(match.range, in: text) { let url = String(text[range]).trimmingCharacters(in: .whitespacesAndNewlines) if ShareViewController.isValidWebsiteURL(url) { return url } } } } catch let error { print("Do-Try Error: \(error.localizedDescription)") } } } catch { print("Error handling incoming text: \(error)") } return nil }
方法2:仅在主Actor中访问NSItemProvider
如果不想让整个函数都在主Actor运行,可以只把调用loadItem的代码块切换到主Actor上下文:
private func handleIncomingURL(itemProvider: NSItemProvider) async -> String? { do { let url = await MainActor.run { try? await itemProvider.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) as? URL } if let url = url, let urlString = url.absoluteString?.trimmingCharacters(in: .whitespacesAndNewlines), ShareViewController.isValidWebsiteURL(urlString) { return urlString } } catch { print("Error handling incoming URL: \(error)") } return nil } private func handleIncomingText(itemProvider: NSItemProvider) async -> String? { do { let text = await MainActor.run { try? await itemProvider.loadItem(forTypeIdentifier: UTType.text.identifier, options: nil) as? String } if let text = text?.trimmingCharacters(in: .whitespacesAndNewlines) { do { let detector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) let matches = detector.matches( in: text, options: [], range: NSRange(location: 0, length: text.utf16.count) ) for match in matches { if let range = Range(match.range, in: text) { let url = String(text[range]).trimmingCharacters(in: .whitespacesAndNewlines) if ShareViewController.isValidWebsiteURL(url) { return url } } } } catch let error { print("Do-Try Error: \(error.localizedDescription)") } } } catch { print("Error handling incoming text: \(error)") } return nil }
额外优化:用URL替代NSURL
NSURL是Objective-C兼容类型,本身也不是Sendable,换成Swift原生的URL不仅符合现代Swift编码风格,还能消除额外类型转换带来的潜在安全警告。
内容的提问来源于stack exchange,提问作者Cheok Yan Cheng
相关产品推荐
相关产品推荐

