Как безопасно получить доступ к содержимому свойства NSArray из вторичного потока?

У меня есть приложение (использующее сохранение/выпуск, а не GC), которое поддерживает переменную экземпляра NSArray, которая отображается как свойство следующим образом:

@interface MyObject : NSObject
{
    NSArray* myArray;
}
@property (copy) NSArray* myArray;
@end

Я хочу получить доступ к содержимому этого массива из вторичного потока, который отсоединен с помощью -performSelectorInBackground:withObject:. Возможно и даже вероятно, что массив изменится во время выполнения вторичного потока.

Во вторичном потоке я хочу сделать что-то вроде этого:

if([self.myArray containsObject:foo])
{
    //do stuff
}

Из чтения документации по потокам кажется, что я должен использовать директиву @synchronized в средствах доступа следующим образом:

@implementation MyObject
- (NSArray *)myArray
{
    NSArray *result;
    @synchronized(self)
    {
        result = [myArray retain];
    }
    return [result autorelease];
}

- (void)setMyArray:(NSArray *)aMyArray
{
    @synchronized(self)
    {
        [myArray release];
        myArray = [aMyArray copy];
    }
}
@end

Это все, что мне нужно сделать, чтобы обеспечить безопасность потоков, или это более сложно?

Обновление: впоследствии я нашел на сайте Apple отличную статью, в которой подробно рассматривается этот вопрос: http://developer.apple.com/mac/library/technotes/tn2002/tn2059.html


person Rob Keniger    schedule 11.02.2010    source источник


Ответы (1)


Ваш приведенный выше код защищает вас от одновременной установки массива или получения массива, пока другой его устанавливает. Поскольку это неизменяемый массив, это прекрасно защищает сам массив.

Однако, если под «массив изменится» вы подразумеваете, что будете редактировать элементы внутри массива, у вас все равно могут возникнуть некоторые проблемы. Например, если массив был заполнен NSMutableStrings, и у вас был запущенный поток:

NSMutableString *foo = [myObject.myArray objectAtIndex:0];
[foo appendString:@"foo"];

и еще один бегал

NSMutableString *bar = [myObject.myArray objectAtIndex:0];
[bar appendString:@"bar"];

Доступ к массиву был бы безопасным (один поток должен был бы ждать доступа к нему другого), однако доступ к указателю foo/bar (что то же самое) не был бы безопасным, так как оба вызова 'appendString' вне блока @synchronized.

Если ваш массив будет меняться именно так, вам нужно будет синхронизировать и эти точки доступа. Либо с большим количеством блоков @synchronized, либо с другими типами блокировок. См. Использование блокировок

person bobDevil    schedule 11.02.2010
comment
Спасибо, это именно то, что я искал. В моем случае элементы в массиве являются неизменяемыми объектами NSString, поэтому мне не нужно беспокоиться о том, что какие-либо элементы в массиве мутируют. - person Rob Keniger; 11.02.2010
comment
Вместо объявления свойства @property (copy) NSArray* myArray; и самостоятельного написания блоков @synchronized, почему бы просто не использовать @property (atomic, copy) NSArray* myArray;? - person Cajunluke; 11.02.2010
comment
CajunLuke: ключевого слова atomic нет, потому что свойства атомарны по умолчанию. - person ; 11.02.2010
comment
Однако остается основной вопрос — зачем использовать тривиальные пользовательские методы доступа? @синтезировать мой массив; будет делать то же самое, только более эффективно. - person Jens Ayton; 11.02.2010
comment
Аруман попал в самую точку. Свойство объявлено для поддержки атомарного доступа. И bobDevil тоже прав: этого недостаточно, чтобы гарантировать потокобезопасность. Если вы когда-нибудь задавались вопросом, достаточно ли этого, чтобы гарантировать потокобезопасность? то ответ всегда нет. Только когда вы доказали, что ваш код является потокобезопасным, этого достаточно, и только до тех пор, пока вы не напишете больше кода. Потокобезопасность — очень и очень хрупкое свойство. (И не забывайте про отсутствие взаимоблокировок, отсутствие голодания, живучесть, инверсию приоритетов... Если вам совершенно не нужны потоки, то не лезьте туда.) - person Jeremy W. Sherman; 13.02.2010