Должен ли каждый ивар быть собственностью?
Я вижу, что он рекомендуется повсюду при кодировании iOS, что свойства должны использоваться для доступа к переменным экземпляра из-за преимуществ, которые это придает, в частности, управлению памятью.
Этот совет не очень со мной. Я считаю, что использование свойств вместо простых старых иваров требует слишком много кода, и я не вижу преимуществ, если вам удобно управлять памятью. Это действительно так важно? Каков ваш подход к управлению переменными экземпляра?
Ответы
Ответ 1
Не нужно декларировать свойства для всех ivars. Приходят несколько моментов:
- Если ivar будет назначаться только один раз за время жизни объекта, вы ничего не получите, объявив свойство. Просто сохраните/скопируйте/назначьте во время
init
, а затем отпустите при необходимости dealloc
.
- Если ivar будет часто изменяться, объявление свойства и всегда использование аксессуаров упростит работу с ошибками управления памятью.
- Вы можете объявлять свойства в расширении класса в файле .m, а не в файле .h, если свойства и ivars должны быть частными.
- При настройке iOS 4.0+ вам не нужно объявлять ivars вообще в своем заголовке, если вы определяете свойство и синтезируете аксессоры.
Таким образом, я обычно использую свойства, но для таких вещей, как NSMutableArray
, которые объект выделяет во время init
, и использует для хранения кучки whattervers, я буду использовать простой старый ivar, так как я никогда не буду переназначать Ивар.
Ответ 2
В то время как ответ Даниила верен, я думаю, что он пропустил важный момент. А именно:
Я считаю, что вместо этого вместо простых старых иваров просто слишком много кода, и я действительно не вижу преимущества, если вам нравится управления памятью.
Преимущества - это последовательность; последовательное управление памятью и последовательное поведение.
Примечательно, что эти две строки кода на самом деле могут иметь совершенно другое поведение во время выполнения:
iVar = [foo retain];
self.iVar = foo;
Первая - это прямая настройка переменной экземпляра, и никаких уведомлений об изменениях не будет. Второй проходит через сеттер и, таким образом, сохраняет любые настройки подкласса при наборе и гарантирует, что любые наблюдатели свойства будут уведомлены об изменении.
Если вы используете ivars прямо во всем своем коде (внутри класса - если вы используете ivars экземпляра непосредственно извне этого экземпляра, хорошо... любой подрядчик, работающий над вашей кодовой базой, должен удвоить свои ставки;), то вы должны либо обрабатывать распространение уведомлений об изменениях вручную (обычно, вызывая willChangeValueForKey:
/didChangeValueForKey
), либо явно разрабатывая ваше приложение, чтобы избежать использования механизмов, которые полагаются на наблюдение за ключевыми значениями.
Вы говорите: "берет слишком много кода". Я этого не вижу; в приведенных выше двух строках кода синтаксис точек меньше символов. Даже вызов метода setter с использованием традиционного синтаксиса будет меньше кода.
И не уменьшайте ценность централизованного управления памятью; одно случайное упущение в бесчисленном множестве сайтов для звонков и разбившегося города.
Ответ 3
Собственность - это просто синтаксический сахар, который позволяет вам снова и снова писать одни и те же методы.
С помощью свойства у вас есть сеттер, который освобождает старый объект и сохраняет новый бесплатно.
Ответ 4
Для частных полей - я рекомендую использовать прямые ivars только для примитивных типов (BOOL/int/float и т.д.). Я нахожу, что хорошая практика обертывает все, связанную с управлением памятью в свойствах - даже редко используемыми полями. Дополнительным преимуществом этого подхода является то, что IDE обычно выделяет прямой доступ к ivars по-разному, поэтому вы всегда имеете хорошее разделение простых скалярных полей и полей типа объекта.
В противовес этому я бы решительно отверг любые прямые ivars в открытом интерфейсе класса. Из-за динамического характера языка это может привести к ошибкам во время выполнения, которые чрезвычайно сложно найти, локализовать и исправить. Рассмотрим следующую иерархию
@interface BaseControl
...
@end
@interface Label : BaseControl
...
@end
@interface Button : BaseControl {
@public
BOOL enabled;
}
@end
и фрагмент кода
- (void)enableAllButtons {
NSArray *buttons = [self getAllButtons]; // expected to contain only Button instances
for (Button *button in buttons) {
button->enabled = YES;
}
}
Теперь представьте себе ошибку где-нибудь в -getAllButtons логике, и вы также получите некоторую метку, возвращенную в этом массиве, - поэтому экземпляры класса Label получат отсутствующий ivar. Тот факт, что может быть неожиданным, заключается в том, что в этом случае не будут отменены. Но в этот момент внутренняя структура экземпляров Label повреждена, и этот будет вызывать поведение undefined и сбой, когда они используются в другом месте.
Как и некоторые популярные проблемы с управлением памятью (и в целом - с оборванными указателями) - такие проблемы трудно найти и локализовать, потому что появление ошибки обычно отдалено (с точки зрения времени, кода или потока приложения ) с места, вызывая ошибку. Но с этой конкретной проблемой у вас даже нет удобных инструментов (таких как анализаторы утечек/зомби и т.д.), Чтобы помочь вам локализовать и исправить ее - даже когда вы узнаете, как ее воспроизвести, и можете легко исследовать ошибочное состояние.
Очевидно, что если вы используете @property (assign) BOOL enabled;
, вы сможете легко диагностировать и исправить исключение во время выполнения в -enableAllButtons.