Home > Mobile >  Use only `await` instead `await MainActor.run { }` for @MainActor properties
Use only `await` instead `await MainActor.run { }` for @MainActor properties

Time:04-17

Let assume a ObservableObject as the following:

class NavigationModel: ObservableObject {
  @MainActor @Published name: String = ""
}

When changing the published property within an async code block running not in the main queue, I am normally using the following syntax:

await MainActor.run {
  name = "foobar"
}

However, I have realised that the following syntax can also be compiled without errors:

await name = "foobar"

I am wondering if this short path is valid and provides same results?

CodePudding user response:

Yes, the two snippets will behave the same. The await will cause the current asynchronous call to "hop" the main actor and execute there.

The compiler (and Swift runtime) know that name can only be accessed from the main thread/actor, so will require you to either:

  1. Access the property from an asynchronous block with @MainActor context.
  2. Hop to the main actor from the current thread to execute there. Then, resume execution of the current function afterwards (however, the function could resume on a different thread than it was originally executing on prior to the hop to the main actor).

Your first snippet is essentially doing both of these steps, introducing a @MainActor scope to run your code in, then you are hopping to the main actor to execute it before continuing with your function call. The second snippet is just skipping the scope creation part, running the single line of code on the main actor, then hopping back right away.

If you're running multiple things on the main actor you will want to reduce the number of hops that are performed, because this will introduce a lot of overhead if you are hopping back-and-forth to and from the main actor. For example:

@MainActor func someUIOperation() async { ... }

func expensiveLotsOfHopsToMainActor() async {
    for _ in 0..<100 {
        await someUIOperation()
    }
}

func betterOnlyOneHopToMainActor() async {
    await MainActor.run {
        for _ in 0..<100 {
           someUIOperation()
        }
    }
}

See examples and more in the original pitch for @MainActor.

  • Related