How can I fix this leaking memory?

This simple example is leaking memory. Especially when the button is pressed.

I think it's got something to do with the way I'm creating the ViewModel.

Please could someone kindly tell my why and how to fix it.

//  MyTestApp.swift

@main
struct MyTestApp: App {

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

// ContentView.swift

struct ContentView: View {
    @StateObject var vm =  ViewModel()
    @State var tog = false
    var body: some View {
        VStack {
            Text(vm.myObject.number)
                .padding()
            Button(action: {
                vm.toggleButton(truth: tog ? "True" : "False")
                tog.toggle()
            }, label: {
                Text("Toggle")
            })
            Text(vm.myObject.truth)
                .padding()
        }
        .frame(width: 300, height: 200)
        .environmentObject(vm)
    }
}

// ViewModel.swift

struct MyObject {
    var number = ""
    var truth = ""
}

class ViewModel: ObservableObject {
    @Published var myObject = MyObject()
    private var timer: Timer?

    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [self] timer in
            getRandonNumber()
        }
    }

    func getRandonNumber() {
            let n = Int.random(in: 100...999)
            myObject.number = "number is \(n)"
    }

    func toggleButton(truth: String) {
        myObject.truth = truth

    }
}
Answered by EA7K in 687158022

Having experimented with every conceivable variation of this very simple app, I can only conclude the problem is within Apple's Swift Compiler. It's a compiler bug they don't tell us about. I suspected every app in the App Store is also eating memory without releasing it. Nobody cares or even notices, because the idiots who have the iPhone glued to their hands only use these app for a short time. Try developing a real app for macOS with lots of buttons and this issue becomes very serious.

So I've answered my own question.

I'm currently developing on Big Sur. Can anyone tell me if Apple have fixed this in any of the latest beta versions?

I have tried your code with Leaks and no memory leak is reported even if I tap the button hundreds of times. Can you clarify how you have checked it and how to reproduce the issue?

Did you try:

   init() {
        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] timer in
            self?.getRandonNumber()
        }
    }

Capturing self is alway „dangerous“ and can lead to memory leaks. The timer/closure keeps a reference to the ViewModel and the ViewModel keeps a reference to the timer, therefore their memory is never released. I don't know if your ViewModel is recreated many times during the run of your app, but if it where, this reference cycle would lead to a memory leak.

To and Dirk-FU. I was wrong to say memory leaks. The issue is memory heap or allocation, or both. The Timer thing is interesting, but it make no noticeable difference.

I added a an init() with a print statement to ViewModel and ContentView. They only instantiate once.

I've also tried compiling a release version. According to Activity monitor, this has a smaller footprint to begin with, but slowly grows, and by even more as the button is pressed.

The thing is, this is a little test app to try and figure out what's going on in my real app. My real app has over 40 Buttons, 8 Pickers with dynamic arrays, and a graphical 'spectrum scope' with a graticule and a filled polygon shape. All interacting with 2 network sources in real time. This does have memory leaks and heap allocation issues - at about 100kB per second.

I've been experimenting where each class is instantiated - without any success so far.

As you can tell, I'm a novice. It would help if Apple's documentation told me what's going on. For example, they say the UI runs on the main thread, but they don't say if that applies in a Button closure. In Activity Monitor I can see more threads than I would have expected.

I can't see anything really broken about your code. Only .environmentObject(vm) in the body of ContentView is not necessary.

Accepted Answer

Having experimented with every conceivable variation of this very simple app, I can only conclude the problem is within Apple's Swift Compiler. It's a compiler bug they don't tell us about. I suspected every app in the App Store is also eating memory without releasing it. Nobody cares or even notices, because the idiots who have the iPhone glued to their hands only use these app for a short time. Try developing a real app for macOS with lots of buttons and this issue becomes very serious.

So I've answered my own question.

I'm currently developing on Big Sur. Can anyone tell me if Apple have fixed this in any of the latest beta versions?

How can I fix this leaking memory?
 
 
Q