Как безопасно получить доступ к содержимому свойства NSArray из вторичного потока? - PullRequest
3 голосов
/ 11 февраля 2010

У меня есть приложение (использующее retain / release, а не 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

1 Ответ

5 голосов
/ 11 февраля 2010

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

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

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

и еще один, который бежал

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

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

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

...