Why is a Class faster in SwiftUI than a Struct? (with example)

Hi all,

can someone more experienced or smarter than me please take a look at this example code?
I was struggling with performance issues using SwiftUI with an MTKView and found out that the Struct I was using to keep the bindings was the guilty one. After hours of searching and trying stuff, I found out that using a Class solved the performance issues. It introduces other issues though, like the UI not always updating everywhere, and all my sliders returning to zero when my macOS app window is minimised or has no focus.
Even though I understand the difference between value and reference types, and that copying a Struct each time takes CPU cycles, I don't understand why my Class values seem to be unreachable at times by the UI...

Here's the macOS example code:

Code Block
//
// ContentView.swift
// SlowStructFastClass
//
// Created by Michel Storms on 12/01/2021.
//
import SwiftUI
class VC {
var s1: Double = 0.5
}
struct ContentView: View {
@State var valueStruct: Double = 0.5
@State var valueClass = VC()
var body: some View {
VStack {
Text("STRUCT")
Slider(value: $valueStruct)
Text("\(self.valueStruct)")
Text("")
Text("CLASS")
Slider(value: $valueClass.s1)
Text("\(self.valueClass.s1)")
}
}
}


I think this is the simplest I could make the example, yet there is still a big speed difference between struct and class binding-based sliders...
Any clues? What am I doing wrong?
Thanks for your time!
(tested on a 2020 i5 MBP running Big Sur)

Answered by stormychel in 661814022
UPDATE:

This never really got solved, not by me and not by Apple, so I decided to go with a Class and update the view by adding a bool named "triggerViewUpdate" to my AppState class, and doing a "triggerViewUpdate.toggle()" whenever needed.

This might not be the most elegant solution, but it works well enough for my use case.

The second example, @State var valueClass = VC() is not typically, or ever done. The "state" being created is the result of creating an object by VC() which is a reference (or pointer) to a VC object. It's a memory address and it won't change, even if the properties it holds do change. Since there's no change to it, the view doesn't update.

Your third example @ObservedObject var valueClassObserved = VD() is also problematic and not typically done. ObservedObjects are intended to observe changes within objects not owned by the view. Here the object is being created and owned by the view.

What you are looking for is either the first example, just using a @State and value type or @StateObject if you want to track an object that you create in the view. So in your example just change ObservedObject to StateObject. StateObject is intended to keep track of objects that are created and owned by the view.

To summarize: If you are initializing the object inside your View code, use @StateObject. If it's initialized outside your View code, and you pass it as argument or otherwise, use @ObservedObject (or @EnvironmentObject, which is more convenient in global cases)





did you ever found a workaround this? i am experiencing the same performance degradation on tvOS when trying to 'swipe' and update really fast a timestamp text, i tried using a @State / @StateObject / @Binding / @Observable (which is a class) and nothing got me even close,

if i get rid of the text and only update a location indicator (moving a rectangle + image + another rectangle as a background filler, using offset) - that works perfect, but as soon as i insert Text everything is back to choppy

Why is a Class faster in SwiftUI than a Struct? (with example)
 
 
Q