Capturing `self` in the body of a `Task` started from within a `deinit`

i ran into a perplexing memory corruption bug today, which turned out to be because i was referencing self from within a Task started inside a deinit:

deinit
{
    Task.init
    {
        doSomethingWith(uuid: self.uuid)
        // `self.uuid` contains garbage data
    }
}

the problem went away when i copied the property to a local variable:

deinit
{
    let uuid:UUID = self.uuid
    Task.init
    {
        doSomethingWith(uuid: uuid)
    }
}

it makes sense to me why self is no longer in an initialized state once the child task begins execution. but why did this ever compile to begin with? why is self allowed to escape a deinit?

From the last time this was brought up:


FYI the preferred way to do this is to put the variable in the closure's capture list.

deinit {
    Task { [uuid] in
        doSomething(with: uuid)
    }
}
7 Likes