State that contains a class (example: an enum with associated values) will retain the class until another state change

When using as a State in view that contains a class object (as an example an Enum with associated values, or directly the class as a nillable state): When the body will trigger a state change, that should deallocate that object (as an example setting the enum to another value that do not contains the object as the associated value, or setting the state class to nil), the object is not deallocated If then the state change another time, setting the state to a third different value (ex: setting enum to a third case) the class object will be then deallocated. In case of not passing through a third state (ex. alternating nil and not nil object) the first object will be deallocated just before the new one is initiated.

This behaviour is not consistent with Swift memory retain system, State apparently is masking a retain inside its implementation, and no documentation appears to show this problem

I may suppose that the problem may due to the "onChange" modifier that will advertise a view of its old and new values, so state may retain the old class until the onChange is fired, but it should be smarter then the current behaviour

Any suggestions for a workaround? radar filed as FB13614543

You can reproduce using the following code


class MyClass {
    init() {
        print("Allocate")
    }

    deinit {
        print("Deallocate")
    }
}

enum LocalState {
    case normal
    case withClass(MyClass)
    case third
}

struct ContentView: View {
    
    @State
    var localState: LocalState = .normal

    @State
    var oldState: LocalState?

    var body: some View {
        switch localState {
        case .normal:
            Button("Go To Class") {
                self.localState = .withClass(MyClass())
            }
        case .withClass:
            Button("Go To Third") {
                self.localState = .third
            }
        case .third:
            Button("Go To Normal") {
                self.localState = .normal
            }
        }
    }
}

@main
struct DemoApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Replies

I've opened a TSI, the developer relations confirmed that it should be a bug in SwiftUI, but I've not been told workarounds or hope for a fix