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 }