Why does `DisclosureGroup` retains object references when content is hidden?

I'm displaying file structure using DisclosureGroup.
And I encountered a memory leak problem.

Code

Node

Node represents a file or a folder.
isExpanded is used to indicate if the child nodes are visible.
If true, it will find its child nodes, which are set into children.
If false, it will clear children for releasing references.

class Node: ObservableObject, Identifiable, Hashable, CustomStringConvertible {

    // ...

    @Published var name: String
    @Published var children: [Node]?

    @Published var isExpanded = false {
        willSet {
            if self.isFile {
                // This node represents a file.
                // It does not have any children.
                return
            }
            if newValue {
                if children?.count == 0 {
                    DispatchQueue.main.async {
                        // get child nodes
                        self.children = childrenOf(self.url)
                    }
                }
            } else {
                if children?.count != 0 {
                    DispatchQueue.main.async {
                        // collapse child nodes
                        self.children?.forEach { child in
                            child.isExpanded = false
                        }
                        // clear children when this node is collapsed
                        self.children = []
                    }
                }
            }
        }
    }

    init(/*...*/) {
        // ...
        print("init \(name)")
    }

    deinit {
        // ...
        print("deinit \(name)")
    }

    // ...
}

For convenience, I print some messages when initializing Node and deinitializing Node.

TreeNode

TreeNode displays Node using DisclosureGroup.

struct TreeNode: View {
    @ObservedObject var parent: Node
    @ObservedObject var node: Node

    var body: some View {
        if node.isFile {
            Text(node.name)
        } else {
            DisclosureGroup(
                isExpanded: $node.isExpanded,
                content: {
                    if node.isExpanded {
                        ForEach(node.children ?? []) { child in
                            TreeNode(parent: node, node: child)
                        }
                    }
                },
                label: {
                    FolderNodeView(node: node)
                }
            )
        }
    }
}

struct FolderNodeView: View {
    @ObservedObject var node: Node

    var body: some View {
        Label(
            title: { Text(node.name) },
            icon: { Image(systemName: "folder.fill") }
        )
    }
}

I use if node.isExpanded for lazy loading.
When node.isExpanded is true, it will show node's children and print initialization messages. Otherwise, it will hide child nodes and print deinitialization messages.

But unexpectedly it does not print any deinitialization messages when the node is collapsed. This indicates that it retains references and therefore these Node objects still exists in memory causing memory leak.

Demo

When the node is expanded, its child nodes will be displayed after loading is completed. The code works correctly.

Then I collapsed the node, it didn't print any deinitialization messages. And when I expanded it again, it initialized new nodes and deinitialized the old nodes at this time. Deinitialization seems to be delayed.

So I guess TreeNode retains references when content is hidden.

Then I deleted TreeNode in ForEach.

DisclosureGroup(
    isExpanded: $node.isExpanded,
    content: {
        if node.isExpanded {
            ForEach(node.children ?? []) { child in
                // TreeNode(parent: node, node: child)
            }
        }
    },
    label: {
        FolderNodeView(node: node)
    }
)

It cannot display the child nodes. But it released reference correctly. So the code works expectedly.

After that, I tried to replace TreeNode with Text or Label. I found that none of them released references immediately when I collapsed the node.

Why did this happen?
Any idea how to fix it?

Why does `DisclosureGroup` retains object references when content is hidden?
 
 
Q