# Is MainActor.assumeIsolated truly necessary in deinit for a @MainActor annotated class?

**URL:** <https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921>\
**Category:** Discussion\
**Tags:** concurrency, actors, using-swift\
**Created:** [July 3, 2024, 4:30pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921 "2024-07-03T16:30:44Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jon\_Reed](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_reed/32/36234_2.png) [@Jon\_Reed](https://forums.swift.org/u/Jon_Reed)\
**Post date:** [July 3, 2024, 4:30pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/1 "2024-07-03T16:30:44Z")

</div>

Hi all,

We are adopting Swift 6 concurrency mode in our project, and I wanted to get a better understanding of how we can better handle deinit for @MainActor annotated classes.

According to answers here: [Deinit and MainActor](https://forums.swift.org/t/deinit-and-mainactor/50132) as well as discussion here: [swift-evolution/proposals/0327-actor-initializers.md at main · swiftlang/swift-evolution · GitHub](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0327-actor-initializers.md) deinit cannot be isolated to an actor because the last reference to the actor could go out of scope on an arbitrary thread.

This of course makes sense if deinit is running code that requires an escaped self or if it is attempting to do things on a _different_ actor, but if the scope of the deinitializing class is only ever running code isolated to itself, on the MainActor, why is this a problem, and what is the solution?

For example in iOS development, it is common to do some cleanup related to Notification observers in deinit on a ViewController. UIViewController is @MainActor, and the app itself will be instantiated on the MainActor, so the deinitialization should never go out of scope of the main thread.

Should we just put MainActor.assumeIsolated in all of those areas, or are there cases where we could expect deinit to be run from an arbitrary thread? What is the best practice here?

Thanks!

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [July 3, 2024, 4:47pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/2 "2024-07-03T16:47:04Z")

</div>

`@MainActor` doesn’t force all references to the object to be held by the main actor — it’s quite common for such objects to be e.g. captured in closures or held by notification sources.

---

<div class="post-metadata">

**Author:** ![Jon\_Reed](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_reed/32/36234_2.png) [@Jon\_Reed](https://forums.swift.org/u/Jon_Reed)\
**Post date:** [July 3, 2024, 4:55pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/3 "2024-07-03T16:55:01Z")

</div>

That makes sense, but what is the correct approach to handle this safely without causing a potential queue assertion failure or escaping self via `Task { @MainActor in }` ? Conceptually it seems a bit unbalanced that we can make guarantees about initialization but not deinitialization.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [July 3, 2024, 5:15pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/4 "2024-07-03T17:15:28Z")

</div>

It's something we'd like to have a cleaner story for, for sure.

First, I'd like to strongly recommend against using `assumeIsolated` for this purpose. If that blows up in your face eventually, which it seems like it eventually would, you probably won't have any good options left. `assumeIsolated` is really for use cases where you do know statically that you're being called from that actor, but you just can't express it to the compiler for whatever reason.

The right way to avoid escaping self during `deinit` is just to pull the values you want to use into local variables and then capture those instead of trying to read them from `self`. That does require a lot of care; this is something that Swift should be diagnosing for you. That is probably your best option until we have some better language support in place.

Personally, I also try to generally discourage people from relying on doing cleanup in `deinit` instead of triggering it during some explicit teardown step. I know that's not a universal opinion, though.

---

<div class="post-metadata">

**Author:** ![Jon\_Reed](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_reed/32/36234_2.png) [@Jon\_Reed](https://forums.swift.org/u/Jon_Reed)\
**Post date:** [July 10, 2024, 6:02pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/5 "2024-07-10T18:02:14Z")

</div>

Your feedback is very helpful, thank you!

I think for right now we'll try to move things out of `deinit` and into the latter portions of the view lifecycle where possible.

I would love to see language support for isolation of deinitialization, or even as you stated simply more diagnostics to make the picture clearer. For understandable reasons this is tricky, but I think it would go a long way.

---

<div class="post-metadata">

**Author:** ![Muhieddine-El-Kaissi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/muhieddine-el-kaissi/32/37246_2.png) [@Muhieddine-El-Kaissi](https://forums.swift.org/u/Muhieddine-El-Kaissi)\
**Post date:** [September 11, 2024, 5:35pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/6 "2024-09-11T17:35:04Z")

</div>

I was able to fix the issue by using dispatch queue to make sure it's on the main thread and executed without delay

```swift
    deinit {
        func cleanup() {
            MainActor.assumeIsolated {
                // call to MainActor code
            }
        }
        if Thread.isMainThread {
            cleanup()
        } else {
            DispatchQueue.main.async {
                cleanup()
            }
        }
    }

```

---

<div class="post-metadata">

**Author:** ![mattie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mattie/32/13267_2.png) [@mattie](https://forums.swift.org/u/mattie)\
**Post date:** [September 12, 2024, 4:03pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/7 "2024-09-12T16:03:50Z")

</div>

Doing some kind of async cleanup is a good pattern, just don't forget that is _absolutely critical_ you not extend the lifetime of `self` when doing it.

This problem is covered, a bit, in the migration guide as well:

[https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/commonproblems#Non-Isolated-Deinitialization](https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/commonproblems#Non-Isolated-Deinitialization)

---

<div class="post-metadata">

**Author:** ![Muhieddine-El-Kaissi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/muhieddine-el-kaissi/32/37246_2.png) [@Muhieddine-El-Kaissi](https://forums.swift.org/u/Muhieddine-El-Kaissi)\
**Post date:** [September 13, 2024, 9:06pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/8 "2024-09-13T21:06:47Z")

</div>

async did extend the life but replacing it with sync fixed the issue but there is a concern that would lock somehow the main thread

---

<div class="post-metadata">

**Author:** ![Arma](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/arma/32/38048_2.png) [@Arma](https://forums.swift.org/u/Arma)\
**Post date:** [September 15, 2024, 7:38pm UTC](https://forums.swift.org/t/is-mainactor-assumeisolated-truly-necessary-in-deinit-for-a-mainactor-annotated-class/72921/9 "2024-09-15T19:38:04Z")

</div>

This code does not work: if the thread is not main, you dispatch a work item to the main queue but **return immediately**. Once `deinit` returns, `self` (may) point to garbage memory and you will run into issues. Please edit your answer as to avoid anyone using your solution with `DispatchQueue.main.async`.
