Должен ли каждый ивар быть собственностью?

Я вижу, что он рекомендуется повсюду при кодировании 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.