2

У меня есть класс со свойством объявлено:Atomic свойства с пользовательским добытчиком

@property (nonatomic, strong) NSMutableArray *links; 

Я хочу лениво инстанцирую так иметь следующий пользовательский поглотитель:

- (NSMutableArray *)links { 

    if (!_links) { 
     _links = [NSMutableArray array]; 
    } 

    return _links; 
} 

Мое приложение несколько прогрессировало и теперь объект может быть доступен из разных потоков. Изменить декларацию:

@property (atomic, strong) NSMutableArray *links; 

Это создает предупреждение компилятора: Writeable атомно свойство не может объединить синтезированный сеттер с определенным пользователем геттером.

Что я понимаю - я думаю. Мой вопрос: правильный сделать следующее, чтобы создать атомную собственность с пользовательским геттером?:

  • создать свою собственную переменную экземпляра _links
  • Wrap мой добытчик в @synchronized
  • Создание определенного пользователем сеттер, также завернутые в @synchronized

EDIT: это код мой новый пользовательский сеттер и геттер:

- (NSMutableArray *)links { 

    if (!_links) { 
     @synchronized(self) { 
      if (!_links) { 
       _links = [NSMutableArray array]; 
      } 
     } 
    } 

    return _links;  
} 

- (void)links:(NSMutableArray *)links { 
     @synchronized(self) { 
     _links = links; 
    } 
} 

ответ

-2
@property (atomic, getter=yourGetter,setter=yourSetter:) NSMutableArray *links; 
+1

Привет, Бритто, спасибо за публикацию, но он не отвечает на вопрос о том, как написать потокобезопасный пользовательский геттер. –

+0

@MagicBulletDave - Лучшее, что я могу сказать, вы попросили атомный пользовательский геттер. Я думаю, что это дает декларация. Вы задали другие вопросы, и этот ответ не пытается ответить на них. Так что это частичный ответ на некоторые из ваших вопросов. Надеюсь, я не ошибаюсь. – jww

+0

Привет, нет, вы не ошибаетесь в вещах - вопрос скорее о правильном шаблоне, который нужно использовать для написания (а не объявления) атомного пользовательского getter. Надеюсь, что это прояснится (я не ответил этим ответом BTW). С уважением, Дэйв. –

0

Я удивляюсь, почему вы хотите сеттер. И мне интересно, почему вы хотите, чтобы свойство было атомарным.

Самый простой и правильный путь, если вы хотите геттер и сеттер, чтобы написать

- (NSMutableArray*)links 
{ 
    @synchronized (self) 
    { 
     if (_links != nil) _links = [[NSMutableArray alloc] init]; 
    } 
    return _links; 
} 

- (void)setLinks:(NSMutableArray*)links 
{ 
    @synchronized (self) 
    { 
     _links = links; 
    } 
} 
+1

У вас есть чек в приемнике назад. Вы хотите создать экземпляр нового массива только в том случае, если '_links' ** является **' nil'. –

+0

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

2

Во-первых, просто потому, что свойство может быть доступен из нескольких потоков, не означает, что а) она должна быть атомарным, или чтобы b) сделать его атомарным, чтобы сделать его потокобезопасным.

В частности, тип свойства NSMutableArray*, что означает, что любой вызывающий абонент получает изменяемый массив и может мутировать его без ограничений, что по своей сути является небезопасным. Вы должны в основном никогда сделать недвижимость с изменяемым типом. Все изменения в свойстве должны пройти через методы доступа, чтобы вы могли контролировать или, как минимум, реагировать на изменения.

Во-вторых, ваш геттер использует double-checked lock anti-pattern. Это не безопасно на языках C, таких как Objective-C. Не делай этого.

Сказанное: вы правы, что если вы сделаете свойство атомарным, и вы реализуете либо геттер, либо сеттер, тогда вы должны реализовать оба эти объекта, и вы также отвечаете за реализацию синхронизации для обеспечения атомарности. Использование @synchronized() подходит, если вы не используете метод двойной проверки блокировки.

Но вам действительно нужно сделать класс класс потокобезопасным по отношению к этому свойству (и любые другие, к которым можно получить доступ из нескольких потоков), что требует более чем атомарного свойства.

+0

Спасибо за отличный совет Кен. Я новичок в разработке многопоточных приложений. Я прочитал статью по ссылке, и я все еще вижу, почему шаблон двойной проверки не является потокобезопасным. Мой план, основанный на том, что вы остаетесь, заключается в том, чтобы скрыть свойство и сделать методы доступа. Тем не менее, я все равно хочу, чтобы ленивый экземпляр массива, можете ли вы дать какие-либо указания о том, как это сделать в потоковом безопасном? –

+0

Языки, такие как C, имеют довольно слабую модель памяти, а компиляторы, процессоры и кеши могут действительно делать очень удивительные вещи для доступа к памяти. Считывание и запись в память не обязательно происходит в том порядке, в котором они отображаются в коде. Так, например, поток A может находиться внутри блока '@ synchronized' в getter. Он может фактически писать и делать видимыми для других потоков указатель в '_links' * до того, как * содержимое записанной на память было написано и доступно другим потокам. Итак, поток B может прийти и найти, что '_links' не является' 'nil', но когда он пытается его использовать, он выходит из строя. –

+0

Что касается создания класса потокобезопасного, то нет простого рецепта для общего случая. Вам нужно будет объяснить, что представляет класс и что он делает. Что такое интерфейс (помимо свойства 'links') и как он будет использоваться? –