Mobile Development 8 min read

Understanding the Underlying Implementation of @synchronized in iOS

This article explains how the @synchronized directive works in iOS as a recursive mutex, demonstrates its usage with code examples, and delves into the low‑level implementation involving objc_sync_enter, objc_sync_exit, id2data, and the associated caching mechanisms.

JD Retail Technology
JD Retail Technology
JD Retail Technology
Understanding the Underlying Implementation of @synchronized in iOS

Introduction

@synchronized is a commonly used lock in iOS development that acts as a recursive mutex, automatically handling lock acquisition and release without explicit programmer intervention, thus avoiding deadlocks and offering higher readability compared to manual NSLock usage.

How @synchronized Works

The expression inside the parentheses provides the lock token (any object such as self or a custom object). When the block begins, the current thread acquires the lock; after the block finishes, the lock is released, allowing other threads to proceed.

Key Questions

1) How is the lock associated with the object passed to @synchronized? 2) What happens if the object parameter is nil ?

Low‑Level Implementation

By stepping through main.m in the debugger, we can see that @synchronized translates to calls to objc_sync_enter before the protected code and objc_sync_exit after it.

int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
NSLog(@"====begin===synchronized");
@synchronized (appDelegateClassName) {
NSLog(@"-======test-synchronized--");
}
NSLog(@"====end===synchronized");
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

The source of objc_sync_enter and objc_sync_exit (found in libobjc ) shows that they first check whether the passed object is non‑nil, then convert it to a SyncData structure via id2data . The SyncData contains a linked‑list pointer, the original object, a thread count, and a mutex that provides the recursive locking behavior.

The id2data function searches for existing SyncData in three places: a fast thread‑local cache, a per‑thread SyncCache , and finally a global list. If none is found, it creates a new entry and stores it in the caches for future fast lookup.

Supporting structures such as SyncCache hold per‑thread lists of SyncData and lock counts, enabling efficient retrieval and release of locks.

Summary

When an object is supplied to @synchronized, its memory address serves as a key that maps to a system‑wide recursive lock; passing nil results in no locking, making the code non‑thread‑safe. Although @synchronized is easy to use, its internal implementation involves complex caching and synchronization mechanisms that developers should understand, especially regarding object lifetimes and nil parameters.

mobile developmentiOSSynchronizationobjc_sync_enterrecursive lock
JD Retail Technology
Written by

JD Retail Technology

Official platform of JD Retail Technology, delivering insightful R&D news and a deep look into the lives and work of technologists.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.