iosswift5reactive-swift

Swift: What will happen to DispatchQueue.async call when caller viewController is deinit()


I am working on a ViewController where I called my viewModel to do a DispatchQueue.async call. After the async task starts and before the end my task ViewController is deinited by pressing the back button. In that case, what will be happened to my async task? Is it gonna hold a thread or a memory block or cause a memory leak? If that happens, is there any way to cancel my async task?

For understanding, I am adding demo classes:

Class A: ViewController {
    let viewModel = B()

    func callViewModelAsyncFunction() {
       viewModel.viewModelAsyncFunction()
    }
}

Class B {
    private let sessionQueue = DispatchQueue(label: "Session.Queue")
    private let sessionGroup = DispatchGroup()
    
    func viewModelAsyncFunction() {
       sessionQueue.async {
           self.sessionGroup.wait()
           self.sessionGroup.enter()
        
           //do my other task signal producer call {
           //}
       }
    }
}

Solution

  • Since no one goona respond to my naive question, I do some experiments, and here is my experiment code on Playgrounds

    import Foundation
    
    
    final class ClassA {
        let viewModel: ViewModel
        let classB: ClassB
        let time: Int
        init(viewModel: ViewModel, classB: ClassB, time: Int) {
            print("Init Class A")
            self.viewModel = viewModel
            self.classB = classB
            self.time = time
        }
        
        func doBackGroundTaskInViewModel() {
            viewModel.doBackGroundTask(anotherClass: classB, completion: {
                print("Block task is done")
            })
        }
        
        deinit {
            
            print("Class A deinited and initTime \(time)")
        }
    }
    
    final class ClassB {
        let time: Int
        init(time: Int) {
            print("Init Class B")
            self.time = time
        }
        
        deinit {
            print("Class B deinited and InitTime:\(time)")
        }
        
        func printClassBInformation() {
            sleep(2)
            print("Claas B still alive")
            printCurrentTime()
            
        }
    }
    
    final class ViewModel {
        private let sessionQueue = DispatchQueue(label: "Session.Queue")
        private let sessionGroup = DispatchGroup()
        func doBackGroundTask(anotherClass: ClassB, completion:  @escaping() -> Void) {
            sessionQueue.async {
                DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                    printCurrentTime()
                    anotherClass.printClassBInformation()
                    completion()
                }
            }
        }
        
        init() {
            print("Init Class ViewModel")
        }
        deinit {
            print("ViewModel deinitd")
        }
    }
    
    func printCurrentTime () {
        let date = Date()
        let calendar = Calendar.current
        print(calendar.component(.second, from: date))
    }
    
    
    
    var classA: ClassA? = ClassA(viewModel: .init(), classB: .init(time: 1), time: 1)
    printCurrentTime()
    
    classA?.doBackGroundTaskInViewModel()
    
    classA = ClassA(viewModel: .init(), classB: .init(time: 2), time: 2)
    classA?.doBackGroundTaskInViewModel()
    classA = nil
    

    And experiment result is

    Init Class ViewModel
    Init Class B
    Init Class A
    51
    Init Class ViewModel
    Init Class B
    Init Class A
    Class A deinited and initTime 1
    ViewModel deinitd
    Class A deinited and initTime 2
    ViewModel deinitd
    53
    Claas B still alive
    55
    Block task is done
    Class B deinited and InitTime:1
    55
    Claas B still alive
    57
    Block task is done
    Class B deinited and InitTime:2
    

    From my experiment, I understand that

    1. Async call can remain in the thread until thread-related task is completed although the caller class is deinited.
    2. Also, another point notices that async call-related variables and completion block hold the Memory.