¿Cómo se bloquea / desbloquea @synchronized en Objective-C?

¿@Synchronized no usa “bloquear” y “desbloquear” para lograr la exclusión mutua? ¿Cómo se bloquea / desbloquea?

La salida del siguiente progtwig es solo “Hello World”.

@interface MyLock: NSLock @end @implementation MyLock - (id)init { return [super init]; } - (void)lock { NSLog(@"before lock"); [super lock]; NSLog(@"after lock"); } - (void)unlock { NSLog(@"before unlock"); [super unlock]; NSLog(@"after unlock"); } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; MyLock *lock = [[MyLock new] autorelease]; @synchronized(lock) { NSLog(@"Hello World"); } [pool drain]; } 

La sincronización del nivel de lenguaje Objective-C usa el mutex, al igual que NSLock . Semánticamente hay algunas pequeñas diferencias técnicas, pero es básicamente correcto pensar en ellas como dos interfaces separadas implementadas en la parte superior de una entidad común (más primitiva).

En particular, con un NSLock tiene un locking explícito, mientras que con @synchronized tiene un locking implícito asociado con el objeto que está utilizando para sincronizar. El beneficio del locking del nivel de idioma es que el comstackdor lo entiende, por lo que puede ocuparse de los problemas del scope, pero mecánicamente se comportan básicamente de la misma manera.

Puedes pensar en @synchronized como una reescritura del comstackdor:

 - (NSString *)myString { @synchronized(self) { return [[myString retain] autorelease]; } } 

se transforma en:

 - (NSString *)myString { NSString *retval = nil; pthread_mutex_t *self_mutex = LOOK_UP_MUTEX(self); pthread_mutex_lock(self_mutex); retval = [[myString retain] autorelease]; pthread_mutex_unlock(self_mutex); return retval; } 

Eso no es exactamente correcto porque la transformación real es más compleja y usa lockings recursivos, pero debe transmitir el punto.

En Objective-C, un bloque @synchronized maneja el locking y el deslocking (así como las posibles excepciones) automáticamente para usted. El tiempo de ejecución genera dinámicamente un NSRecursiveLock que está asociado con el objeto en el que se está sincronizando. Esta documentación de Apple lo explica con más detalle. Es por eso que no está viendo los mensajes de registro de su subclase NSLock: el objeto en el que se sincroniza puede ser cualquier cosa, no solo un locking NS.

Básicamente, @synchronized (...) es una construcción de conveniencia que agiliza su código. Como la mayoría de las abstracciones simplificadoras, tiene una sobrecarga asociada (piense en ello como un costo oculto), y es bueno tenerlo en cuenta, pero el rendimiento en bruto probablemente no sea el objective supremo al usar tales construcciones de todos modos.

Actualmente

 { @synchronized(self) { return [[myString retain] autorelease]; } } 

se transforma directamente en:

 // needs #import  { objc_sync_enter(self) id retVal = [[myString retain] autorelease]; objc_sync_exit(self); return retVal; } 

Esta API disponible desde iOS 2.0 e importada usando …

 #import  

La implementación de Apple de @synchronized es de código abierto y se puede encontrar aquí . Mike Ash escribió dos publicaciones realmente interesantes sobre este tema:

  • Bloqueos, Seguridad de subprocesos y Swift
  • Construyamos @sincronizado

En pocas palabras, tiene una tabla que mapea punteros de objeto (usando sus direcciones de memoria como claves) para pthread_mutex_t lockings, que se bloquean y desbloquean según sea necesario.

Simplemente asocia un semáforo con cada objeto y lo usa.