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

793 Views Asked by At

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 {
           //}
       }
    }
}
1

There are 1 best solutions below

0
Md Abul Kashem On BEST ANSWER

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.