Swift6并发报错处理
1、@MainActor
使用@MainActor
相当于设置所有操作将在主线程。因此就没有并发问题了。
@MainActor
var id = UUID()
@MainActor
func test() async {
id = UUID()
}
func test() {
Task {
await MainActor.run {// 在主线程操作
}
}
}
func test() {
DispatchQueue.main.async {[weak self] in
//在主线程操作
}
}
2、@globalActor
创建自己的全局Actor
,将所有对全局变量的读写限制在特定的Actor
之中。
@globalActor
actor MyActor {
static let shared = MyActor()
}
@MyActor
var id = UUID()
@MyActor
func test() async {
id = UUID()
}
@MyActor
struct Model {
static var id = UUID()
func test() async {
Self.id = UUID()
}
}
3、mutex
在不使用actor增加异步代码时,可以使用mutex保护数据。
- Mutex 是阻塞式的,适合用于快速、同步的状态访问;
- 使用 Mutex 不会强制引入异步调用,因此不会干扰调用栈结构;
- Mutex 本身符合 Sendable,可以很好地融入 Swift 6 的并发模型。
class Counter {
private let mutex = Mutex(0)
func increment() {
mutex.withLock { count in
count += 1
}
}
func decrement() {
mutex.withLock { count in
count -= 1
}
}
}
4、nonisolated(unsafe)
直接告诉编译器这些代码不会引发数据竞争,有崩溃算我的,请忽略此问题。
nonisolated(unsafe)
var id = UUID()
func test() async {
id = UUID()
}
5、@preconcurrency
@preconcurrency
主要用于暂时关闭 Swift 6 的严格并发检查。
1.比如引入的第三方库不支持Swift6:
@preconcurrency import UIKit
2.忽略某个方法的并发检查
@preconcurrency
func myOldFunction() {
}
3.忽略某个类的并发检查
@preconcurrency
class MyViewController: UIViewController {
func goToNextScreen() {
navigationController?.pushViewController(NextViewController(), animated: true)
}
}
6、Sendable协议
主要用于并发编程,确保数据可以安全地在线程之间传递,避免数据竞争(Data Race)问题。以下类型隐式地自动遵守
- 基础类型,Int、String、Bool 等;
- 不含有引用类型成员的 struct;
- 不含有引用类型关联值的 enum;
- 所含元素类型符合 Sendable 协议的集合,如:Array、Dictionary 等。
class 需要主动声明遵守 Sendable 协议,并有以下限制:
class 必须是 final,否则有 Warning: Non-final class 'X' cannot conform to 'Sendable'; use ' @unchecked Sendable'
class 的存储属性必须是 immutable,否则有 Warning: Stored property 'x' of 'Sendable'-conforming class 'X' is mutable
class 的存储属性必须都遵守 Sendable 协议,否则 Warning: Stored property 'y' of 'Sendable'-conforming class 'X' has non-sendable type 'Y'
class 的祖先类 (如有) 必须遵守 Sendable 协议或者是 NSObject,否则 Error: 'Sendable' class 'X' cannot inherit from another class other than 'NSObject'。
或者使用 @unchecked Sendable
告诉编译器忽略检查
class User: @unchecked Sendable {
var name: String
var age: Int
}
7、@Sendable
被@Sendable
修饰的函数、闭包可以跨 actor 传递。
8、 MainActor.assumeIsolated
在 Swift 6,MainActor.assumeIsolated {}
允许你在 非 async
环境 下安全地访问 @MainActor
标记的变量,避免编译器报错,但不会进行线程检查(开发者需要确保代码真的在主线程运行)。
🌟 适用场景
- 你在一个 同步(非
async
)函数 里,想要访问@MainActor
隔离的变量(比如UIDevice.current.name
)。 - 你 知道代码已经在主线程运行,但编译器仍然报错 "Main actor-isolated property cannot be referenced from a nonisolated context"。
- 你 不能让函数变成
async
,比如在Moya
这样的第三方库里。
/// 设备系统版本
@MainActor static var phoneSystemVersion: String {
return UIDevice.current.systemVersion
}
let v = MainActor.assumeIsolated { WKAppDevice.phoneSystemVersion }