🙆‍♀️

【Swift】MainActor で隔離されたクラスの deinit を MainActor で実行する

に公開

MainActor で隔離されたクラスの deinit の前にisolatedをつけることで、 deinit を MainActor で実行させることができます。

isolated deinit { ... }
サンプル
struct ContentView: View {
    @State private var isShowingFirstView = false
    
    var body: some View {
        VStack {
            if isShowingFirstView == true {
                FirstView()
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .overlay(alignment: .bottom) {
            Button("Action") {
                isShowingFirstView.toggle()
            }
        }
    }
}

struct FirstView: View {
    @State private var vm = FirstViewModel()
    
    var body: some View {
        Text("This is SecondView.")
            .onAppear(perform: vm.onAppear)
    }
}

@MainActor
final class FirstViewModel: ObservableObject {
    @Published private(set) var task: Task<Void, Never>? = nil
        
    /// メインアクターで実行されるのでこれでOK
    ///
    isolated deinit {
        task?.cancel()
    }
    
//    deinit {
//        Task { @MainActor [weak self] in
//            self?.task?.cancel
//        }
//    }
    
    func onAppear() {
        self.task = Task {
            await asyncAction()
        }
    }
}

func asyncAction() async {
    for i in 0..<100 {
        do {
            try await Task.sleep(for: .seconds(0.5))
            print(i)
            continue
        } catch is CancellationError {
            print("cancell")
        } catch {
            print(error)
        }
        
        break
    }
}

参考:What's new in Swift 6.2?

Discussion