objective-c - nsdictionary to object swift
NSMutableDictionary thread safety (4)
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;
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 .
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.
después de un poco de investigación, quiero compartir con ustedes este artículo:
Uso de clases de recopilació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.
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 bloqueo:
// 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 bloqueo:
@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 bloqueo 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.