Swift vs. Objective-C Global Variable

3 minute read

Published:

In WWDC 2024’s Swift 6 Migration, Ben from Apple’s Swift team mentioned that there is a big difference for the global variable (mutatble) between Objective-C and Swift.

Question

Between Swift and Objective-C global variables, which one will be in the risk of a data race? And which one is thread-safe?

Anwser

Objective-C global variable is not thread-safe and it’s prune to data race. Swift global variable is thread-safe but it’s also prune to data race.

Demo

Why They Are Both Prune To Data Race?

Objective-C Global Variables

Creation Time

  • Static Initialization: In Objective-C, global variables are typically initialized at program startup, before the main function is executed. This is known as static initialization.
  • Compile Time: The compiler allocates memory for global variables, and their initialization occurs as the binary is loaded into memory by the runtime.

Data Race Risk

  • Data Race Risk: Objective-C global variables can be prone to data races because they are not inherently thread-safe. If multiple threads attempt to read and write to a global variable simultaneously, it can lead to inconsistent or incorrect states.
  • Synchronization Required: To prevent data races, explicit synchronization mechanisms like locks or GCD (Grand Central Dispatch) are needed when accessing global variables across multiple threads.

Swift Global Variables

Creation Time

  • Lazy Initialization: In Swift, global variables are lazily initialized. This means that the global variable is not created until it is first accessed. This is managed by a dispatch_once-like mechanism internally, ensuring the variable is initialized only once.
  • Deferred Initialization: This lazy initialization helps in reducing the initial memory footprint and avoids unnecessary initialization if the global variable is never used.

Data Race Risk

  • Thread-Safety with Lazy Initialization: Swift’s lazy initialization of global variables is thread-safe. The runtime ensures that even if multiple threads attempt to access the global variable simultaneously, the variable will be initialized only once.
  • Data Race Risk Reduced: Although the initialization process is thread-safe, the use of global variables in Swift can still lead to data races if the variable is mutable and accessed concurrently without proper synchronization.
  • Synchronization Needed for Mutable Variables: For mutable global variables, developers must ensure thread-safe access by using synchronization mechanisms such as dispatch queues or locks to avoid data races.

Example Comparison

Objective-C

// Objective-C global variable
int globalVariable = 0;

// Function to increment the global variable
void incrementGlobalVariable() {
    globalVariable++;
}

Swift

// Swift global variable
var globalVariable: Int = 0

// Function to increment the global variable
func incrementGlobalVariable() {
    globalVariable += 1
}

In both cases, if incrementGlobalVariable is called from multiple threads simultaneously, there is a risk of a data race. However, in Swift, the initial creation of globalVariable is thread-safe due to its lazy initialization, while in Objective-C, this is not a consideration since the variable is initialized at program startup.

Summary

  • Creation Time:
    • Objective-C: Global variables are initialized at program startup.
    • Swift: Global variables are lazily initialized upon first access, ensuring thread-safe initialization. This means Swift will have faster app launch time in this perspective.
  • Data Race Risk:
    • Objective-C: Global variables can lead to data races if accessed concurrently without synchronization.
    • Swift: While the lazy initialization is thread-safe, mutable global variables still require explicit synchronization to avoid data races during concurrent access.

Fix Swift 6 Concurrency Warning

If all the callers are from main thread / main actor, we can add annotation

// Swift global variable
@MainActor var globalVariable: Int = 0

[last resort] We can also let consumer be cautious by adding

// Swift global variable
nonisolated(unsafe) var globalVariable: Int = 0