Ответ
Безопасность достигается через корректное использование свойств (@property) вместо прямого доступа к переменным экземпляра (ivars).
Рекомендуемый подход:
@interface MyClass : NSObject
// Объявление свойств обеспечивает инкапсуляцию и управление памятью.
@property (nonatomic, strong) NSString *name;
@property (atomic, copy) NSArray *items;
@end
Преимущества использования @property:
- Управление памятью: Автоматические retain/release/сopy (атрибуты
strong,weak,copy). - Потокобезопасность: Свойство
atomic(по умолчанию) обеспечивает атомарность геттера/сеттера. - Кастомизация: Возможность переопределения сеттеров и геттеров.
- KVO/KVC: Поддержка технологий наблюдения и кодирования.
Когда допустим прямой доступ к ivars (_variable):
- В методах
initиdealloc(во избежание побочных эффектов от KVO/кастомных сеттеров). - В критичных по производительности участках кода (после профилирования).
- В реализациях геттеров/сеттеров самого свойства.
Для многопоточности:
- Используйте
atomicсвойства для простых случаев. - Для сложной синхронизации применяйте
dispatch_queue,@synchronizedили другие примитивы, даже сnonatomicсвойствами.
Ответ 18+ 🔞
Да ты посмотри, какая история! Всё у них там с безопасностью, с потоками, с доступом... Ну прямо как в хорошем банке, блядь, сейфы да коды. А на самом деле, если разобраться, всё просто, как три копейки.
Вот смотри, есть у тебя класс, да? И ты туда пихаешь свои данные — имя там, массив какой-нибудь. Так вот, самый правильный способ, чтобы тебя потом не обвинили в распиздяйстве — это объявить свойства, ёпта! Не лезь напрямую к этим внутренним переменным, как слон в посудную лавку.
@interface MyClass : NSObject
@property (nonatomic, strong) NSString *name;
@property (atomic, copy) NSArray *items;
@end
Вот так, красиво и под замком. А почему? А потому что:
- Память сама за собой убирает. Сказал
strong— он держит, сказалcopy— он копию сделает, чтоб тебе никто извне твой массив не испоганил. Удобно, блядь! - Потоки не перебьют друг другу ебало. Поставил
atomic(он там по умолчанию, кстати) — и можно не бздеть, что в один момент один поток читает, а другой в это же время пишет какую-то хуйню. Геттер с сеттером работают аккуратненько. - Хочешь — кастомизируй. Захотел при установке имени ещё и логировать это дело — пожалуйста, переопредели сеттер, и делай там что вдумается.
- KVO/KVC сразу дружат. Эти технологии наблюдения за твоими данными сразу понимают, куда смотреть.
А когда можно, как свинья, лезть прямо в _variable?
- В
initиdealloc. Тут главное — не накосячить. Пока объект рождается или умирает, не надо вызывать кастомные сеттеры, они могут там ещё не готовы или уже отвалились. Работай напрямую с железом, чтобы не было сюрпризов. - Если уж совсем овердохуища критичный по скорости кусок кода. Но это только после того, как профилировщик тебе конкретно показал, что свойство — бутылочное горлышко. А так — не выёживайся.
- Ну и внутри самого геттера/сеттера этого свойства — там и так понятно, что обращаешься к хранилищу.
Про потоки отдельная песня.
На atomic уповаешь — хорошо, для простых случаев хватит. Но если у тебя там сложная логика, несколько свойств связаны, или операция не одна — тогда, друг мой, бери в руки dispatch_queue, оборачивай в @synchronized, делай как человек. Даже если свойства nonatomic. Потому что atomic — это про атомарность доступа к одному полю, а не про то, чтобы группа полей всегда была в согласованном состоянии. Не обманывайся, а то будет потом волнение ебать, где баг искать.
Короче, правило простое: объяви свойство, работай через точку, спи спокойно. А в особых случаях — аккуратно, с прямой наводкой. И всё будет пучком.