Swift6并發(fā)報(bào)錯(cuò)處理
1、@MainActor
使用@MainActor相當(dāng)于設(shè)置所有操作將在主線程。因此就沒有并發(fā)問(wèn)題了。
@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
創(chuàng)建自己的全局Actor,將所有對(duì)全局變量的讀寫限制在特定的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增加異步代碼時(shí),可以使用mutex保護(hù)數(shù)據(jù)。
- Mutex 是阻塞式的,適合用于快速、同步的狀態(tài)訪問(wèn);
- 使用 Mutex 不會(huì)強(qiáng)制引入異步調(diào)用,因此不會(huì)干擾調(diào)用棧結(jié)構(gòu);
- Mutex 本身符合 Sendable,可以很好地融入 Swift 6 的并發(fā)模型。
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)
直接告訴編譯器這些代碼不會(huì)引發(fā)數(shù)據(jù)競(jìng)爭(zhēng),有崩潰算我的,請(qǐng)忽略此問(wèn)題。
nonisolated(unsafe)
var id = UUID()
func test() async {
id = UUID()
}
5、@preconcurrency
@preconcurrency 主要用于暫時(shí)關(guān)閉 Swift 6 的嚴(yán)格并發(fā)檢查。
1.比如引入的第三方庫(kù)不支持Swift6:
@preconcurrency import UIKit
2.忽略某個(gè)方法的并發(fā)檢查
@preconcurrency
func myOldFunction() {
}
3.忽略某個(gè)類的并發(fā)檢查
@preconcurrency
class MyViewController: UIViewController {
func goToNextScreen() {
navigationController?.pushViewController(NextViewController(), animated: true)
}
}
6、Sendable協(xié)議
主要用于并發(fā)編程,確保數(shù)據(jù)可以安全地在線程之間傳遞,避免數(shù)據(jù)競(jìng)爭(zhēng)(Data Race)問(wèn)題。以下類型隱式地自動(dòng)遵守
- 基礎(chǔ)類型,Int、String、Bool 等;
- 不含有引用類型成員的 struct;
- 不含有引用類型關(guān)聯(lián)值的 enum;
- 所含元素類型符合 Sendable 協(xié)議的集合,如:Array、Dictionary 等。
class 需要主動(dòng)聲明遵守 Sendable 協(xié)議,并有以下限制:
class 必須是 final,否則有 Warning: Non-final class 'X' cannot conform to 'Sendable'; use ' @unchecked Sendable'
class 的存儲(chǔ)屬性必須是 immutable,否則有 Warning: Stored property 'x' of 'Sendable'-conforming class 'X' is mutable
class 的存儲(chǔ)屬性必須都遵守 Sendable 協(xié)議,否則 Warning: Stored property 'y' of 'Sendable'-conforming class 'X' has non-sendable type 'Y'
class 的祖先類 (如有) 必須遵守 Sendable 協(xié)議或者是 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修飾的函數(shù)、閉包可以跨 actor 傳遞。
8、 MainActor.assumeIsolated
在 Swift 6,MainActor.assumeIsolated {} 允許你在 非 async 環(huán)境 下安全地訪問(wèn) @MainActor 標(biāo)記的變量,避免編譯器報(bào)錯(cuò),但不會(huì)進(jìn)行線程檢查(開發(fā)者需要確保代碼真的在主線程運(yùn)行)。
?? 適用場(chǎng)景
- 你在一個(gè) 同步(非
async)函數(shù) 里,想要訪問(wèn)@MainActor隔離的變量(比如UIDevice.current.name)。 - 你 知道代碼已經(jīng)在主線程運(yùn)行,但編譯器仍然報(bào)錯(cuò) "Main actor-isolated property cannot be referenced from a nonisolated context"。
- 你 不能讓函數(shù)變成
async,比如在Moya這樣的第三方庫(kù)里。
/// 設(shè)備系統(tǒng)版本
@MainActor static var phoneSystemVersion: String {
return UIDevice.current.systemVersion
}
let v = MainActor.assumeIsolated { WKAppDevice.phoneSystemVersion }