Swift concurrency
Objective-C
synchronisation
multithreading
Swift programming

What is the Swift equivalent to Objective-C's synchronized?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Swift does not have a direct language feature equivalent to Objective-C's @synchronized block. Instead, Swift offers several concurrency tools, and the best replacement depends on whether you are protecting shared mutable state in legacy code, coordinating work on a queue, or using the modern actor-based concurrency model.

What @synchronized Did in Objective-C

In Objective-C, @synchronized wrapped a critical section and locked on an object reference. It was convenient because the lock and unlock behavior was automatic, but it also hid some complexity and overhead.

objective-c
@synchronized(self) {
    [self.items addObject:newItem];
}

If you are migrating old code, the first question is not "what is the Swift syntax equivalent?" but "what concurrency model should this code use now?"

Use NSLock for Direct Mutual Exclusion

When you need a straightforward lock around shared state in synchronous code, NSLock is the closest conceptual replacement.

swift
1import Foundation
2
3final class SafeStore {
4    private var items: [String] = []
5    private let lock = NSLock()
6
7    func add(_ value: String) {
8        lock.lock()
9        defer { lock.unlock() }
10        items.append(value)
11    }
12
13    func snapshot() -> [String] {
14        lock.lock()
15        defer { lock.unlock() }
16        return items
17    }
18}

Using defer is important because it guarantees unlock even if the method exits early.

Use a Serial DispatchQueue for Ownership-Style Access

If you want all access to a piece of state to happen on one queue, a private serial queue is often cleaner than explicit lock management.

swift
1import Foundation
2
3final class QueueBackedStore {
4    private var items: [String] = []
5    private let queue = DispatchQueue(label: "com.example.store")
6
7    func add(_ value: String) {
8        queue.sync {
9            items.append(value)
10        }
11    }
12
13    func snapshot() -> [String] {
14        queue.sync { items }
15    }
16}

This reads well and avoids manual lock bookkeeping, though you still need to think carefully about reentrancy and calling sync from the same queue.

Prefer Actors in Modern Swift Concurrency

For new Swift code, actors are usually the best high-level replacement because they isolate mutable state by design.

swift
1actor MessageStore {
2    private var items: [String] = []
3
4    func add(_ value: String) {
5        items.append(value)
6    }
7
8    func snapshot() -> [String] {
9        items
10    }
11}

With actors, you do not manually lock and unlock. The compiler and runtime enforce access rules around actor-isolated state, which removes an entire class of mistakes common with traditional locking.

Choose Based on Code Style and Constraints

A practical guideline is:

  • use NSLock when integrating with older synchronous code paths
  • use a serial queue when queue ownership already fits the design
  • use actors for new async-capable Swift code

There is no single universal replacement because @synchronized bundled several concerns into one easy syntax, while Swift encourages more explicit architectural choices.

Avoid Translating Patterns Blindly

Directly translating every @synchronized block into a lock in Swift can preserve old design problems. Sometimes the better migration is to remove shared mutable state entirely, or to isolate it behind an actor or dedicated queue-based component.

That usually leads to code that is easier to reason about, test, and evolve.

Common Pitfalls

  • Locking with NSLock but forgetting defer, which can leave the lock held on early return or failure.
  • Calling sync recursively on the same serial queue and causing a deadlock.
  • Assuming actors are a drop-in replacement for every synchronous API shape.
  • Translating Objective-C locking patterns mechanically instead of redesigning around Swift concurrency.
  • Protecting only writes while leaving reads unsynchronized.

Summary

  • Swift has no exact syntax equivalent to Objective-C's @synchronized.
  • 'NSLock is the closest direct mutual-exclusion replacement.'
  • Serial DispatchQueue access is often cleaner when one queue should own the state.
  • Actors are usually the best option for new Swift concurrency code.
  • Choose the tool based on the code's architecture, not on syntax similarity alone.

Course illustration
Course illustration

All Rights Reserved.