Подтвердить что ты не робот

Должен ли я использовать свойства или прямую ссылку при доступе к переменным экземпляра внутри?

Скажем, у меня есть класс вроде этого:

@interface MyAwesomeClass : NSObject
{
@private
    NSString *thing1;
    NSString *thing2;
}
@property (retain) NSString *thing1;
@property (retain) NSString *thing2;
@end

@implementation MyAwesomeClass
@synthesize thing1, thing1;
@end

При доступе к thing1 и thing2 внутренне (т.е. в рамках реализации MyAwesomeClass) лучше использовать свойство или просто ссылаться на переменную экземпляра напрямую (при условии, что мы не делаем никаких работать в "пользовательском" доступе или мутаторе, т.е. мы просто устанавливаем и получаем переменную). Pre-Objective C 2.0, мы обычно просто получаем доступ к ivars напрямую, но каков обычный стиль кодирования/лучшая практика сейчас? И изменилась ли эта рекомендация, если переменная/свойство экземпляра является частной и вообще недоступной вне класса? Должны ли вы создавать свойство для каждого ivar, даже если они являются частными, или только для данных с открытым доступом? Что делать, если мое приложение не использует функции кодирования ключевых значений (поскольку KVC работает только для доступа к свойствам)?

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

@interface MyAwesomeClass : NSObject
{
    id myObj;
}
@proprety id myObj;
@end

@implementation MyAwesomeClass
@synthesize myObj;
@end

Я знаю, что myObj = anotherObject функционально совпадает с self.myObj = anotherObj.

Но свойства - это не просто причудливый синтаксис для инструктирования компилятора, чтобы, конечно, писать аксессоры и мутаторы; они также способ улучшить инкапсуляцию данных, т.е. вы можете изменить внутреннюю реализацию класса, не переписывая классы, которые полагаются на эти свойства. Меня интересуют ответы, которые затрагивают важность этой проблемы инкапсуляции при работе с собственным внутренним кодом класса. Кроме того, правильно написанные свойства могут запускать уведомления KVC, но прямой доступ к ivar не будет; это имеет значение, если мое приложение не использует функции KVC сейчас, на всякий случай, если это возможно в будущем?

4b9b3361

Ответ 1

Я не думаю, что "лучше". Вы видите оба стиля в общем использовании, поэтому сейчас нет даже обычной/лучшей практики. По моему опыту, используемый стиль очень мало влияет на то, насколько хорошо я перерабатываю некоторый файл реализации, который я ищу. Вы, конечно, хотите быть в курсе обоих стилей (и между ними), глядя на код других людей.

Использование свойства для каждого внутреннего ivar может быть немного за бортом, с точки зрения обслуживания. Я сделал это, и он добавил нетривиальный объем работы, который, как мне кажется, не окупился. Но если у вас есть сильное желание /OCD для просмотра постоянного кода, такого как self.var везде, и у вас есть это в глубине вашего разума каждый раз, когда вы смотрите на класс, а затем используйте его. Не уменьшайте эффект, который может вызвать чувство рвоты при производительности.

Исключения. Очевидно, что для пользовательских геттеров (например, ленивое создание) у вас мало выбора. Кроме того, я создаю и использую свойство для внутренних сеттеров, когда это делает его более удобным (например, установка объектов с помощью семантики собственности).

"на всякий случай", "возможно" не является веской причиной сделать что-то без дополнительных данных, так как время, необходимое для его реализации, отличное от нуля. Лучше может быть вопрос, какова вероятность того, что все частные ивары в каком-то классе потребуют от KVC-уведомлений в будущем, но не сейчас? Для большинства моих собственных классов ответ чрезвычайно низок, поэтому теперь я избегаю жесткого правила о создании свойств для каждого частного ivar.

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

Если вы заинтересованы, мой собственный подход таков:

  • Чтение ivars: прямой доступ, если нет пользовательского getter (например, ленивое создание)
  • Написание ivars: Непосредственно в alloc/dealloc. В другом месте, через частную собственность, если таковая существует.

Ответ 2

Если вы проводите время в списке рассылки cocoa -dev, вы обнаружите, что это очень спорная тема.

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

Некоторые люди думают, что вы всегда должны (или в основном) использовать свойства вместо ivars. Основное преимущество здесь в том, что управление памятью хорошо содержится внутри методов доступа, а не разбросано по вашей логике реализации. Уведомления KVO и побочные эффекты аксессуаров можно преодолеть, создав отдельные свойства, которые указывают на один и тот же ivar.

Глядя на образец кода Apple, вы обнаружите, что они повсюду в этой теме. Некоторые образцы используют свойства внутри, некоторые используют ivars.

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

Ответ 3

Единственное отличие в присваивании thing1 = something; и self.thing1 = something; заключается в том, что если вы хотите, чтобы операция присвоения свойств (retain, copy и т.д.) выполнялась на назначенном объекте, тогда вам нужно используйте свойство. Присвоение без свойств будет эффективно просто: присвоение ссылки на предоставленный объект.

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

Ответ 4

Если thing1 используется с KVO, рекомендуется использовать self.thing1= при его установке. Если thing1 @public, то лучше предположить, что кто-то когда-нибудь захочет использовать его с KVO.

Если thing1 имеет сложную семантику набора, которую вы не хотите повторять везде, где вы ее устанавливаете (например, retain или не nonatomic), то использовать с помощью self.thing1= является хорошей идеей.

Если бенчмаркинг показывает, что вызов setThing1: занимает значительное время, вы можете подумать о способах его установки без использования self.thing1= - возможно, обратите внимание, что он не может быть KVO'ed или посмотреть, KVO лучше (например, если вы установили его 3000 раз в петле где-нибудь, вы можете установить его через self->thing1 3000 раз и сделать 2 KVO-вызова о том, что значение изменится и изменилось).

Это оставляет случай тривиального сеттера в частной переменной, где вы знаете, что вы не используете KVO. В этот момент он перестает быть техническим вопросом и подпадает под стиль кода. По крайней мере, пока аксессор не появится как узкое место в профилировщике. Я имею тенденцию использовать прямой доступ к ivar в этот момент (если только я не думаю, что буду KVO, что значение в будущем, или, возможно, захочет сделать его общедоступным и, таким образом, подумать, что другие могут захотеть KVO).

Однако, когда я устанавливаю вещи с прямым доступом ivar, я стараюсь делать это только через self->thing1=, что намного упрощает их поиск и заменяет их, если я когда-либо нахожу необходимость использовать KVO или сделать это публиковать или создавать более сложный аксессор.

Ответ 5

Другие вещи, упомянутые здесь, все в порядке. Несколько вещей, которые пропустили другие ответы:

Во-первых, всегда имейте в виду, что приемы доступа/мутаторы являются виртуальными (как и все методы Objective-C). В целом было сказано, что следует избегать вызова виртуальных методов в init и dealloc, потому что вы не знайте, что сделает подкласс, что может помешать вам. По этой причине я обычно пытаюсь получить доступ к iVars непосредственно в init и dealloc и получить доступ к ним через accessor/mutators везде. С другой стороны, если вы не всегда используете аксессоры во всех других местах, подклассы, которые их переопределяют, могут быть затронуты.

Кроме того, гарантии свойств атомарности (т.е. ваши @properties объявлены атомарными) не могут поддерживаться никому, если вы обращаетесь к iVar прямо где-либо вне init и dealloc. Если вам нужно что-то атомарное, не выбрасывайте атомарность, обратившись непосредственно к iVar. Аналогичным образом, если вам не нужны эти гарантии, объявите ваше свойство неатомным (для производительности).

Это также относится к проблеме KVO. В init никто не может наблюдать вас еще (законно), а в dealloc любой оставшийся наблюдатель имеет устаревшую неохваченную (то есть фиктивную) ссылку. Те же рассуждения применимы и к гарантиям свойств атомарности. (т.е. как будут выполняться параллельные обращения до возвращения init и доступа, которые происходят во время dealloc, являются по своей сути ошибками.)

Если вы смешиваете и сопоставляете использование прямого и доступного/мутатора, вы рискуете столкнуться не только с KVO и атомарностью, но и с субклассами.