Using Threads in Swift causes memory leak

549 Views Asked by At

I encountered a strange behaviour in my application when using Threads inside my ViewModel. It seems something is happening in Thread.init which causes a memory leak even when the deinit of the Thread object is called (I'm not even starting the thread).

I'm relatively new to Swift so it might be my fault but I don't know why. I have two Views linked together via a NavigationView. The child View has a ViewModel which creates a Thread when the initialization method is called and destroys the Thread object when the deinitialization method is called. The deinitialization method of the ViewModel is called when the child View is "closed". After navigating back to the parent View I can see in the Memory Profiler of Xcode that there is a new memory leak

enter image description here

I post a small example which reproduces this issue below

The HomeView: (main view)

import SwiftUI

struct HomeView: View {
    @State private var showView = false
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: TestView(showView: self.$showView), isActive : self.$showView) {
                    Text("Open MainView")
                }.isDetailLink(false)
                .navigationBarTitle("Home")
            }
        }
    }
}

The TestView: (child view)

import SwiftUI

struct TestView: View {
    
    @Binding var showView : Bool
    
    @StateObject var mainViewModel = TestViewViewModel()
    
    var body: some View {
        Button("Back") {
            showView = false
        }.onAppear(perform: {
            mainViewModel.initViewModel()
        })
        .onDisappear(perform: {
            mainViewModel.deinitViewModel()
        })
    }
}

The ViewModel:

import Foundation

class TestViewViewModel: ObservableObject {
    
    private var testThread: TestThread?
    
    public func initViewModel() {
        testThread = TestThread.init()
    }
    
    public func deinitViewModel() {
        testThread?.cancel()
        testThread = nil
    }
    
    
    /*+++++++++++++++*/
    /*+ Test thread +*/
    /*+++++++++++++++*/

    private class TestThread: Thread {

        override init(){
            print("Init RenderThread")
        }
        
        deinit {
            print("deinit RenderThread")
        }

        override func main() {
            
        }
    }
}

That's it, no magic involved but when clicking the Back button in the child view it seems something will not be garbage collected in the ViewModel's Thread. Any hints what might be wrong? Is this a bug or is there something I have to release manually?

0

There are 0 best solutions below