Должен ли каждый ивар быть собственностью? - PullRequest
55 голосов
/ 17 февраля 2011

Я считаю, что при программировании для iOS везде рекомендуется использовать свойства для доступа к переменным экземпляра из-за преимуществ, которые это дает, среди прочего, управлению памятью.

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

Ответы [ 4 ]

77 голосов
/ 17 февраля 2011

Не обязательно объявлять свойства для всех ivars.На ум приходит несколько моментов:

  • Если ивару назначают только один раз за время существования объекта, вы ничего не получите, объявив свойство.Просто сохраните / скопируйте / назначьте во время init, а затем отпустите, если необходимо, во время dealloc.
  • Если ивар будет часто изменяться, объявление свойства и всегда использование аксессоров будет легче избежатьошибки управления памятью.
  • Вы можете объявить свойства в расширении класса в файле .m, а не в файле .h, если свойства и ivars должны быть частными.
  • При ориентации на iOS 4.0+, вам вообще не нужно объявлять ivars в заголовке, если вы определяете свойство и синтезируете методы доступа.

Поэтому я обычно использую свойства, но для таких вещей, как NSMutableArray, объектвыделяет во время init и использует для удержания кучу whatevers, я буду использовать простой старый ivar, так как я никогда не переназначу ivar.

51 голосов
/ 17 февраля 2011

Хотя ответ Даниэля верен, я думаю, что он упускает важный момент.А именно:

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

Преимущества - последовательность;согласованное управление памятью и согласованное поведение.

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

iVar = [foo retain];
self.iVar = foo;

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

Если выиспользуют ivars непосредственно во всем вашем коде (внутри класса - если вы используете ivars экземпляра непосредственно из за пределами этого экземпляра, что ж ... любой подрядчик, работающий с вашей кодовой базой, должен удвоить свои ставки;)затем вы должны либо обрабатывать распространение уведомлений об изменениях вручную (обычно вызывая willChangeValueForKey: / didChangeValueForKey) или , чтобы явно спроектировать ваше приложение, чтобы избежать использования механизмов, которые полагаются на наблюдение значения ключа.

Вы говорите, "берет слишком много кода".Я не вижу этого;в приведенных выше двух строках кода точечный синтаксис содержит меньше символов.Даже вызов метода сеттера с использованием традиционного синтаксиса будет меньше кода.

И не стоит сбрасывать со счетов значение в централизации управления памятью;одно случайное упущение во множестве сайтов вызовов и в городе, где произошел сбой.

3 голосов
/ 17 февраля 2011

Свойство - это просто синтаксический сахар, который не позволяет вам писать одни и те же методы снова и снова. Со свойством у вас есть сеттер, который освобождает старый объект и сохраняет новый бесплатно.

1 голос
/ 12 июля 2012

Для приватных полей - я предлагаю использовать прямые ивары безопасно только для примитивных типов (BOOL / int / float и т. Д.). Я считаю хорошей практикой обертывание всего , связанного с управлением памятью, в свойствах - даже редко используемых полей. Дополнительным преимуществом этого подхода является то, что IDE обычно выделяет прямой доступ к иварам по-разному, поэтому у вас всегда есть хорошее разделение простых скалярных полей и полей типа объекта.

В противоположность этому, я бы настоятельно не рекомендовал бы любые прямые ivars в классе public interface . Из-за динамической природы языка это может привести к ошибкам во время выполнения, которые чрезвычайно сложно найти, локализовать и исправить. Рассмотрим следующую иерархию

@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, возвращенные в этом массиве - так что этим экземплярам класса Label будет присвоен пропущенный ivar. Факт, который может быть удивительным, заключается в том, что -enableAllButtons не будет аварийно завершать работу в этом случае. Но в этот момент внутренняя структура этих экземпляров Label повреждена , и это вызовет непредсказуемое поведение и вылетит при их использовании в других местах.

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

Очевидно, что если вы используете @property (assign) BOOL enabled;, вы получите простую диагностику и исправление исключения времени выполнения в -enableAllButtons.

...