Подтвердить что ты не робот

Безопасность NSMutableDictionary

У меня есть вопрос о безопасности потоков при использовании NSMutableDictionary.

Основной поток - это данные из NSMutableDictionary где:

  • ключ NSString
  • значение UIImage

Асинхронный поток записывает данные в словарь (используя NSOperationQueue)

Как сделать безопасный поток слов выше?

Должен ли я сделать свойство NSMutableDictionary atomic? Или мне нужно внести какие-либо дополнительные изменения?

@property(retain) NSMutableDictionary *dicNamesWithPhotos;

4b9b3361

Ответ 1

NSMutableDictionary не предназначен для потокобезопасной структуры данных и просто маркирует свойство как atomic, не гарантирует, что операции с базовыми данными фактически выполняются атомарно (безопасным образом).

Чтобы гарантировать, что каждая операция выполняется безопасным образом, вам нужно будет заблокировать каждую операцию в словаре с помощью блокировки:

// 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];

Вам следует рассмотреть возможность переноса собственного NSDictionary, который просто делегирует вызовы в NSMutableDictionary, удерживая блокировку:

@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

Обратите внимание, что поскольку каждая операция требует ожидания блокировки и ее удержания, она не достаточно масштабируема, но может быть достаточно хорошей в вашем случае.

Если вы хотите использовать нужную библиотеку с резьбой, вы можете использовать библиотеку TransactionKit, поскольку они имеют TKMutableDictionary, который является многопоточным безопасным библиотека. Я лично не использовал его, и кажется, что это незавершенная библиотека, но вы можете попробовать.

Ответ 2

после небольшого исследования я хочу поделиться с вами этой статьей:

Безопасное использование классов коллекций с помощью многопоточных приложений http://developer.apple.com/library/mac/#technotes/tn2002/tn2059.html

Похоже, что notnoop ответ не может быть решением в конце концов. С точки зрения резьбы это нормально, но есть некоторые критические тонкости. Я не буду размещать здесь решение, но я думаю, что в этой статье есть хорошая.

Ответ 3

У меня есть два варианта использования nsmutabledictionary.

Один из них:

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

Два:

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

Я не знаю, почему некоторые люди хотят перезаписать настройки и получить mutabledictionary.

Ответ 4

Даже ответ правильный, есть элегантное и другое решение:

- (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;
}

Копия важна при использовании небезопасных объектов потока, при этом вы можете избежать возможной ошибки из-за непреднамеренного выпуска переменной. Нет необходимости в потокобезопасных объектах.

Если больше очереди захочет использовать NSMutableDictionary, объявите приватную очередь и измените установщик на:

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);
    }
});
}

ВАЖНО!

Вы должны установить собственную приватную очередь без него dispatch_barrier_sync - это просто dispatch_sync

Подробное объяснение в этой замечательной статье в блоге.