NSMutableDictionary thread safety

Tengo una pregunta sobre seguridad de subprocesos mientras uso NSMutableDictionary .

El hilo principal es leer datos de NSMutableDictionary donde:

  • la clave es NSString
  • valor es UIImage

Un hilo asíncrono está escribiendo datos en el diccionario anterior (usando NSOperationQueue )

¿Cómo puedo hacer que el hilo de diccionario anterior sea seguro?

¿Debo hacer que la propiedad NSMutableDictionary atomic ? ¿O necesito hacer algún cambio adicional?

@property(retain) NSMutableDictionary *dicNamesWithPhotos;

NSMutableDictionary no está diseñado para ser una estructura de datos segura para subprocesos, y simplemente marcar la propiedad como atomic no garantiza que las operaciones de datos subyacentes se realicen atómicamente (de manera segura).

Para garantizar que cada operación se realice de forma segura, deberá proteger cada operación en el diccionario con un locking:

 // in initialization self.dictionary = [[NSMutableDictionary alloc] init]; // create a lock object for the dictionary self.dictionary_lock = [[NSLock alloc] init]; // at every access or modification: [object.dictionary_lock lock]; [object.dictionary setObject:image forKey:name]; [object.dictionary_lock unlock]; 

Debería considerar rodar su propio NSDictionary que simplemente delega llamadas a NSMutableDictionary mientras mantiene un locking:

 @interface SafeMutableDictionary : NSMutableDictionary { NSLock *lock; NSMutableDictionary *underlyingDictionary; } @end @implementation SafeMutableDictionary - (id)init { if (self = [super init]) { lock = [[NSLock alloc] init]; underlyingDictionary = [[NSMutableDictionary alloc] init]; } return self; } - (void) dealloc { [lock_ release]; [underlyingDictionary release]; [super dealloc]; } // forward all the calls with the lock held - (retval_t) forward: (SEL) sel : (arglist_t) args { [lock lock]; @try { return [underlyingDictionary performv:sel : args]; } @finally { [lock unlock]; } } @end 

Tenga en cuenta que debido a que cada operación requiere esperar el locking y mantenerlo, no es muy escalable, pero podría ser lo suficientemente bueno en su caso.

Si desea utilizar una biblioteca con hilos adecuada, puede usar la biblioteca de TransactionKit ya que tienen TKMutableDictionary que es una biblioteca segura de subprocesos múltiples. Personalmente no lo he usado, y parece que es una biblioteca en progreso, pero es posible que desee probarlo.

después de un poco de investigación, quiero compartir con ustedes este artículo:

Uso de clases de recostackción de forma segura con aplicaciones multiproceso http://developer.apple.com/library/mac/#technotes/tn2002/tn2059.html

Parece que la respuesta de notnoop puede no ser una solución después de todo. Desde la perspectiva de enhebrar, está bien, pero hay algunas sutilezas críticas. No publicaré aquí una solución, pero creo que hay una buena en este artículo.

Tengo dos opciones para usar nsmutabledictionary.

Uno es:

 NSLock* lock = [[NSLock alloc] init]; [lock lock]; [object.dictionary setObject:image forKey:name]; [lock unlock]; 

Dos es:

 //Let's assume var image, name are setup properly dispatch_async(dispatch_get_main_queue(), ^{ [object.dictionary setObject:image forKey:name]; }); 

No sé por qué algunas personas quieren sobrescribir la configuración y obtener de mutabledictionary.

Incluso la respuesta es correcta, hay una solución elegante y diferente:

 - (id)init { self = [super init]; if (self != nil) { NSString *label = [NSString stringWithFormat:@"%@.isolation.%p", [self class], self]; self.isolationQueue = dispatch_queue_create([label UTF8String], NULL); label = [NSString stringWithFormat:@"%@.work.%p", [self class], self]; self.workQueue = dispatch_queue_create([label UTF8String], NULL); } return self; } //Setter, write into NSMutableDictionary - (void)setCount:(NSUInteger)count forKey:(NSString *)key { key = [key copy]; dispatch_async(self.isolationQueue, ^(){ if (count == 0) { [self.counts removeObjectForKey:key]; } else { self.counts[key] = @(count); } }); } //Getter, read from NSMutableDictionary - (NSUInteger)countForKey:(NSString *)key { __block NSUInteger count; dispatch_sync(self.isolationQueue, ^(){ NSNumber *n = self.counts[key]; count = [n unsignedIntegerValue]; }); return count; } 

La copia es importante cuando se usan objetos inseguros con hilos, con esto se puede evitar el posible error debido a la liberación involuntaria de la variable. No es necesario contar con entidades seguras para hilos.

Si a más colas les gustaría usar NSMutableDictionary, declaren una cola privada y cambien el setter a:

 self.isolationQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_CONCURRENT); - (void)setCount:(NSUInteger)count forKey:(NSString *)key { key = [key copy]; dispatch_barrier_async(self.isolationQueue, ^(){ if (count == 0) { [self.counts removeObjectForKey:key]; } else { self.counts[key] = @(count); } }); } 

¡IMPORTANTE!

Debes establecer una cola privada propia sin ella. Dispatch_barrier_sync es solo un simple dispatch_sync

La explicación detallada está en este maravilloso artículo de blog .

Hoy en día probablemente @synchronized(object) .

 ... @synchronized(dictionary) { [dictionary setObject:image forKey:name]; } ... @synchronized(dictionary) { [dictionary objectForKey:key]; } ... @synchronized(dictionary) { [dictionary removeObjectForKey:key]; } 

Ya no es necesario el objeto NSLock