It seems to be impossible to deallocate such a data structure without the Swift runtime freezing.
True. Deallocating an enum instance with indirect child instances would cause deallocating the children recursively.
So, when deallocating long lasting list as in your code, it would make too deep recursive calls which may result any sort of unpredictable behaviors, crashing, freezing or anything you can imagine.
You may need to deallocate all the nodes in the list without consuming the native call stack -- you need non-recursive algorithm.
You cannot control the deallocations of structs and enums in Swift, so you may need to make it a class
and implement deinit
carefully.
My first trial looks like this:
class ListNode {
var left: ListNode?
var right: ListNode?
init(_ left: ListNode?, _ right: ListNode?) {
self.left = left
self.right = right
}
deinit {
if left != nil || right != nil {
var nodesToRelease: [ListNode] = [self]
ListNode.deallocateAll(&nodesToRelease)
}
}
private static func deallocateAll(_ nodesToRelease: inout [ListNode]) {
while let top = nodesToRelease.popLast() {
if let leftNode = top.left {
nodesToRelease.append(leftNode)
top.left = nil
}
if let rightNode = top.right {
nodesToRelease.append(rightNode)
top.right = nil
}
}
}
}
var node: ListNode? = nil
for _ in 0..<150_000 {
node = ListNode(nil, node)
}
print("Done creating list")
node = nil
print("Completed program (this is printed within a second)")
This shows Completed program (this is printed within a second)
within a second.
Changing 150_000
to 150_000_000
, you can observe increasing of the memory consumption first, and then decreasing after Done creating list
.
(Takes a few minutes to finish.)
I haven't tried many cases, so there may be some flaws in my code and may not be efficient. But I think my code is well explaining the problem.
If you know the recursion level of the data might get hundreds or more, you should better not use enum with indirect children.