Même la réponse est correcte, il y a une solution élégante et différente:
- (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 copie est importante lors de l'utilisation fil des objets dangereux, avec cela, vous pouvez éviter l'erreur possible en raison de la libération involontaire de la variable . Pas besoin d'entités thread-safe.
Si plus file d'attente aimerait utiliser le NSMutableDictionary déclarer une file d'attente privée et changer le setter à:
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);
}
});
}
IMPORTANT!
Vous devez définir une propre file d'attente privée sans elle, le dispatch_barrier_sync est un simple dispatch_sync
Explication détaillée est dans ce marvelous blog article.
Je ne suis pas un expert en multithreading, mais je sais que le drapeau "atomic" (par défaut pour les accesseurs @ synthesize'd) ne garantit pas la sécurité des threads. J'ai quand même pensé la même chose quand je l'ai lu pour la première fois. –