我正在尝试重构应用程序的现有实现,使其符合 Swift 6。为此,我必须接触以下提供“在 Safari 中打开 url”共享扩展的类:
import Foundation
import UIKit
public final class SafariActivity: UIActivity {
var url: URL?
// MARK: - UIActivity Implementation
public override var activityType: UIActivity.ActivityType {
.openInSafari
}
public override var activityTitle: String? {
"Open in Safari"
}
public override var activityImage: UIImage? {
UIImage(systemName: "safari")?.applyingSymbolConfiguration(.init(scale: .large))
}
public override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
// ERROR: Main actor-isolated class property 'shared' can not be referenced from a nonisolated context
activityItems.contains { $0 is URL ? UIApplication.shared.canOpenURL($0 as! URL) : false }
}
public override func prepare(withActivityItems activityItems: [Any]) {
// ERROR: Main actor-isolated class property 'shared' can not be referenced from a nonisolated context
url = activityItems.first { $0 is URL ? UIApplication.shared.canOpenURL($0 as! URL) : false } as? URL
}
public override func perform() {
if let url = url {
// ERROR: Call to main actor-isolated instance method 'open(_:options:completionHandler:)' in a synchronous nonisolated context
UIApplication.shared.open(url)
}
self.activityDidFinish(true)
}
}
extension UIActivity.ActivityType {
static let openInSafari = UIActivity.ActivityType(rawValue: "openInSafari")
}
extension UIActivity {
@MainActor public static let openInSafari = SafariActivity()
}
我很难理解将覆盖的方法限制在主线程的正确方法。如何将覆盖的方法从UIActivity
隔离改为@MainActor
?
这里的主要问题是
UIApplication.shared
主要参与者是被隔离的。canOpenURL
是非隔离的,所以那里没有问题。如果您
UIApplication.shared
知道自己处于主要参与者隔离的环境中,则可以将其存储在属性中并使用它。例如:
不幸的是,我们不能将其标记
init
为,@MainActor
因为它会覆盖NSObject.init
,而 是非隔离的。如果这是直接从 继承的NSObject
,那么 Swift 可以知道这是安全的,但是我们继承了UIActivity
,所以 Swift 无法分辨。您还可以添加一个额外的参数来接收
UIApplication
,这样init
就不会覆盖NSObject.init
,然后,您只需将所有 替换为
UIApplication.shared
即可sharedApp
。最后,尽管没有标记为
@MainActor
,perform
但实际上记录为仅在主线程上调用,因此可以安全地使用MainActor.assumeIsolated
。