Skip to content

Commit b3c83dc

Browse files
deinit solution (#49)
* deinit technique #30 * Address feedback
1 parent fcbbec7 commit b3c83dc

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

Guide.docc/CommonProblems.md

+56
Original file line numberDiff line numberDiff line change
@@ -719,3 +719,59 @@ class WindowStyler {
719719
All `Sendable` properties can still be safely accessed in this `init` method.
720720
And while any non-`Sendable` properties cannot,
721721
they can still be initialized by using default expressions.
722+
723+
### Non-Isolated Deinitialization
724+
725+
Even if a type has actor isolation, deinitializers are _always_ non-isolated.
726+
727+
```swift
728+
actor BackgroundStyler {
729+
// another actor-isolated type
730+
private let store = StyleStore()
731+
732+
deinit {
733+
// this is non-isolated
734+
store.stopNotifications()
735+
}
736+
}
737+
```
738+
739+
This code produces the error:
740+
741+
```
742+
error: call to actor-isolated instance method 'stopNotifications()' in a synchronous nonisolated context
743+
5 | deinit {
744+
6 | // this is non-isolated
745+
7 | store.stopNotifications()
746+
| `- error: call to actor-isolated instance method 'stopNotifications()' in a synchronous nonisolated context
747+
8 | }
748+
9 | }
749+
```
750+
751+
While this might feel surprising, given that this type is an actor,
752+
this is not a new constraint.
753+
The thread that executes a deinitializer has never been guaranteed and
754+
Swift's data isolation is now just surfacing that fact.
755+
756+
Often, the work being done within the `deinit` does not need to be synchronous.
757+
A solution is to use an unstructured `Task` to first capture and
758+
then operate on the isolated values.
759+
When using this technique,
760+
it is _critical_ to ensure you do not capture `self`, even implicitly.
761+
762+
```swift
763+
actor BackgroundStyler {
764+
// another actor-isolated type
765+
private let store = StyleStore()
766+
767+
deinit {
768+
// no actor isolation here, so none will be inherited by the task
769+
Task { [store] in
770+
await store.stopNotifications()
771+
}
772+
}
773+
}
774+
```
775+
776+
> Important: **Never** extend the life-time of `self` from within
777+
`deinit`. Doing so will crash at runtime.

0 commit comments

Comments
 (0)