Я обычно вижу этот вопрос, заданный по-другому, например, должен ли каждый ивар быть собственностью? (и мне нравится ответ bbum на этот вопрос).
Я использую свойства почти исключительно в моем коде. Однако очень часто я работаю с подрядчиком, который долгое время разрабатывал для iOS и являлся традиционным программистом игр. Он пишет код, который почти не объявляет свойства и опирается на ivars. Я предполагаю, что он делает это, потому что: 1.) он привык к этому, поскольку свойства не всегда существовали до Objective C 2.0 (октябрь '07) и 2.) для минимального прироста производительности без прохождения через геттер / сеттер.
Хотя он пишет код, который не пропускает, я все же предпочел бы, чтобы он использовал свойства вместо ivars. Мы говорили об этом, и он более или менее не видит причин использовать свойства, поскольку мы не использовали KVO, и у него есть опыт решения проблем с памятью.
Мой вопрос больше ... Почему вы хотите использовать период ивара - опытный или нет. Действительно ли такая большая разница в производительности, что использование ивара было бы оправданным?
Также в качестве пояснения я перезаписываю сеттеры и геттеры по мере необходимости и использую ивар, который соотносится с этим свойством внутри геттера / сеттера. Тем не менее, за пределами getter / setter или init, я всегда используюself.myProperty
синтаксис.
Редактировать 1
Я ценю все хорошие ответы. Единственное, на что я хотел бы обратить внимание, - это то, что с помощью ивара вы получаете инкапсуляцию, а с свойством - нет. Просто определите свойство в продолжении класса. Это скроет собственность от посторонних. Вы также можете объявить свойство только для чтения в интерфейсе и переопределить его как readwrite в реализации, например:
// readonly for outsiders
@property (nonatomic, copy, readonly) NSString * name;
и иметь в классе продолжение:
// readwrite within this file
@property (nonatomic, copy) NSString * name;
Чтобы он был полностью «приватным», объявите его только в продолжении класса.
Ответы:
Инкапсуляция
Если ivar является приватным, другие части программы не могут получить его так легко. С объявленной собственностью умные люди могут легко получить доступ и видоизменяться через средства доступа.
Производительность
Да, это может иметь значение в некоторых случаях. Некоторые программы имеют ограничения, при которых они не могут использовать какой-либо объектный обмен сообщениями в определенных частях программы (думайте в реальном времени). В других случаях вы можете получить доступ к нему напрямую для скорости. В других случаях это происходит потому, что обмен сообщениями objc действует как межсетевой экран оптимизации. Наконец, это может уменьшить количество операций с ссылками и минимизировать пиковое использование памяти (если все сделано правильно).
Нетривиальные типы
Пример: если у вас тип C ++, прямой доступ иногда является лучшим подходом. Тип может не быть копируемым, или копировать может быть нетривиальным.
Многопоточность
Многие из ваших иваров зависят от кода. Вы должны обеспечить целостность данных в многопоточном контексте. Таким образом, вы можете предпочесть прямой доступ к нескольким участникам в критических разделах. Если вы придерживаетесь методов доступа к зависимым от кода данным, ваши блокировки, как правило, должны быть реентерабельными, и вы часто будете совершать гораздо больше приобретений (значительно больше в разы).
Корректность программы
Поскольку подклассы могут переопределять любой метод, вы можете со временем увидеть семантическое различие между записью в интерфейс и соответствующим управлением своим состоянием. Прямой доступ для корректности программы особенно распространен в частично построенных состояниях - в ваших инициализаторах и в
dealloc
них лучше использовать прямой доступ. Вы также можете найти эту общие в реализациях аксессора, конструктор удобства,copy
,mutableCopy
внедрения и архивирования / сериализации.Это также происходит чаще, так как человек переходит от всего, к чему имеет общедоступный образ доступа readwrite, к тому, что хорошо скрывает детали реализации / данные. Иногда вам нужно правильно обходить побочные эффекты, которые может внести переопределение подкласса, чтобы сделать правильные вещи.
Бинарный размер
Объявление всего readwrite по умолчанию обычно приводит ко многим методам доступа, которые вам никогда не понадобятся, если вы задумаетесь о выполнении вашей программы на мгновение. Так что это добавит немного жира в вашу программу и время загрузки.
Минимизирует Сложность
В некоторых случаях просто нет необходимости добавлять + type + для поддержки всех этих дополнительных скаффолдингов для простой переменной, такой как private bool, которая записывается в одном методе и читается в другом.
Нельзя сказать, что использование свойств или методов доступа - это плохо - у каждого есть важные преимущества и ограничения. Как и многие ОО-языки и подходы к проектированию, вы также должны отдавать предпочтение методам доступа с соответствующей видимостью в ObjC. Будут времена, когда вам нужно отклоняться. По этой причине, я думаю, что часто лучше ограничить прямой доступ к реализации, которая объявляет ivar (например, объявляет это
@private
).повторно редактировать 1:
Большинство из нас запомнили, как динамически вызывать скрытый метод доступа (если мы знаем имя…). Между тем, большинство из нас не запомнили, как правильно обращаться к иварам, которые не видны (за пределами KVC). Продолжение класса помогает , но оно создает уязвимости.
Этот обходной путь очевиден:
Теперь попробуйте это только с помощью ивара и без KVC.
источник
@private
, компилятор должен запретить доступ к элементам за пределами методов класса и экземпляра - разве это не то, что вы видите?Для меня это обычно производительность. Доступ к ивару объекта так же быстр, как доступ к элементу структуры в C с использованием указателя на память, содержащую такую структуру. Фактически, объекты Objective C в основном являются структурами C, расположенными в динамически распределенной памяти. Это обычно так быстро, как ваш код, даже сборка, оптимизированная вручную, не может быть быстрее, чем это.
Доступ к ивару через метод получения / настройки включает вызов метода Objective-C, который намного медленнее (по крайней мере, в 3-4 раза), чем «нормальный» вызов функции C, и даже обычный вызов функции C уже будет во много раз медленнее, чем доступ к члену структуры. В зависимости от атрибутов вашего свойства, реализация метода set / getter, сгенерированная компилятором, может включать в себя другой вызов функции C для функций
objc_getProperty
/objc_setProperty
, поскольку они должны будутretain
/copy
/autorelease
выполнять объекты по мере необходимости и дополнительно выполнять спин-блокировку для атомарных свойств, где это необходимо. Это может легко стать очень дорогим, и я не говорю о том, чтобы быть на 50% медленнее.Давайте попробуем это:
Вывод:
Это в 4,28 раза медленнее, и это был неатомарный примитив int, в значительной степени лучший случай ; большинство других случаев еще хуже (попробуйте атомарное
NSString *
свойство!). Таким образом, если вы согласны с тем фактом, что каждый доступ к ivar-файлам в 4-5 раз медленнее, чем он мог бы быть, использование свойств - это нормально (по крайней мере, когда речь идет о производительности), однако, существует множество ситуаций, когда такое снижение производительности совершенно неприемлемо.Обновление 2015-10-20
Некоторые люди утверждают, что это не проблема реального мира, приведенный выше код является чисто синтетическим, и вы никогда не заметите этого в реальном приложении. Хорошо, тогда давайте попробуем пример из реального мира.
Следующий код определяет
Account
объекты. У учетной записи есть свойства, которые описывают имя (NSString *
), пол (enum
) и возраст (unsigned
) ее владельца, а также баланс (int64_t
). У объекта учетной записи естьinit
метод иcompare:
метод.compare:
Метод определяется как: Женский заказы , прежде чем мужчина, имена заказа по алфавиту, молодые заказы до старого, заказы баланса от низкого до высокого.На самом деле существует два класса учетных записей,
AccountA
иAccountB
. Если вы посмотрите на их реализацию, вы заметите, что они почти полностью идентичны, за одним исключением:compare:
метод.AccountA
объекты получают доступ к своим собственным свойствам методом (getter), аAccountB
объекты получают доступ к своим собственным свойствам с помощью ivar. Это действительно единственная разница! Они оба получают доступ к свойствам другого объекта для сравнения с помощью получателя (доступ к нему с помощью ivar не будет безопасным! Что, если другой объект является подклассом и переопределил получатель?). Также обратите внимание, что доступ к вашим собственным свойствам в качестве ivars не нарушает инкапсуляцию (ivars по-прежнему не являются общедоступными).Настройка теста очень проста: создайте случайные учетные записи 1 Mio, добавьте их в массив и отсортируйте этот массив. Вот и все. Конечно, есть два массива, один для
AccountA
объектов и один дляAccountB
объектов, и оба массива заполнены одинаковыми учетными записями (один и тот же источник данных). Мы рассчитываем, сколько времени потребуется для сортировки массивов.Вот вывод нескольких прогонов, которые я сделал вчера:
Как видите, сортировка массива
AccountB
объектов всегда значительно быстрее, чем сортировка массиваAccountA
объектов.Кто бы ни утверждал, что различия во времени выполнения до 1,32 секунды не имеют значения, лучше никогда не заниматься программированием пользовательского интерфейса. Например, если я хочу изменить порядок сортировки большой таблицы, такие различия во времени имеют огромное значение для пользователя (разница между приемлемым и вялым пользовательским интерфейсом).
Кроме того, в этом случае пример кода является единственной реальной работой, выполняемой здесь, но как часто ваш код является всего лишь небольшим механизмом сложного часового механизма? И если каждая передача замедляет весь процесс, как это, что это означает для скорости всего часового механизма в конце? Особенно, если один рабочий шаг зависит от результата другого, что означает, что все неэффективности будут суммироваться. Большинство неэффективностей сами по себе не являются проблемой, их сумма становится проблемой для всего процесса. И такая проблема - это не то, что профилировщик легко покажет, потому что профилировщик занимается поиском критических горячих точек, но ни одна из этих неэффективностей не является горячей точкой сама по себе. Время процессора распределяется между ними в среднем, но каждый из них имеет лишь небольшую часть, и оптимизация кажется пустой тратой времени. И это правда,
И даже если вы не думаете с точки зрения процессорного времени, потому что вы считаете, что тратить процессорное время вполне приемлемо, в конце концов, «это бесплатно», тогда как насчет затрат на хостинг сервера, вызванных энергопотреблением? Как насчет времени автономной работы мобильных устройств? Если вы напишете одно и то же мобильное приложение дважды (например, собственный мобильный веб-браузер), то однажды версия, в которой все классы будут получать доступ к своим свойствам только через геттеры, и однажды, когда все классы будут получать к ним доступ только через ivars, использование первого из них постоянно истощает Батарея намного быстрее, чем при использовании второй, хотя они функционально эквивалентны, а пользователю вторая, вероятно, даже будет немного быстрее.
Теперь вот код для вашего
main.m
файла (код полагается на включение ARC и обязательно используйте оптимизацию при компиляции, чтобы увидеть полный эффект):источник
unsigned int
который никогда не сохраняется / освобождается, используете ли вы ARC или нет. Само сохранение / освобождение является дорогостоящим, поэтому разница будет меньше, так как управление хранением добавляет статические издержки, которые всегда существуют, с использованием метода setter / getter или ivar напрямую; тем не менее, вы все равно сохраните накладные расходы на один дополнительный вызов метода, если получите прямой доступ к ivar. В большинстве случаев не имеет большого значения, если вы не делаете это несколько тысяч раз в секунду. Apple говорит, что по умолчанию используют getter / setters, если вы не используете метод init / dealloc или не обнаружили узкое место.copy
, НЕ будет делать копию своего значения при каждом доступе к нему. Получательcopy
свойства подобен получателюstrong
/retain
собственности. Это код в основномreturn [[self->value retain] autorelease];
. Только установщик копирует значение, и оно будет выглядеть примерно так[self->value autorelease]; self->value = [newValue copy];
, тогда какstrong
/retain
установщик выглядит так:[self->value autorelease]; self->value = [newValue retain];
Наиболее важной причиной является концепция сокрытия информации ООП : если вы выставляете все через свойства и тем самым позволяете внешним объектам просматривать внутренние объекты другого объекта, то вы будете использовать их и таким образом усложнять изменение реализации.
Прибыль «минимальной производительности» может быстро подвести итог и стать проблемой. Я знаю из опыта; Я работаю над приложением, которое действительно доводит iDevices до предела, и поэтому мы должны избегать ненужных вызовов методов (конечно, только там, где это возможно). Чтобы помочь в достижении этой цели, мы также избегаем точечного синтаксиса, поскольку с первого взгляда сложно увидеть количество вызовов методов: например, сколько вызовов методов вызывает выражение
self.image.size.width
? Напротив, вы можете сразу сказать с[[self image] size].width
.Кроме того, при правильном присвоении имен ivar KVO возможно без свойств (IIRC, я не эксперт KVO).
источник
Семантика
@property
может выразить то, что не могут сделать ивары:nonatomic
иcopy
.@property
не могут:@protected
: public на подклассах, private снаружи.@package
: общедоступный на фреймворках на 64 бита, приватный снаружи. То же, что и@public
на 32 битах. См. 64-битный контроль доступа к классам и экземплярам Apple .id __strong *_objs
.Производительность
Короткая история: ивары быстрее, но это не имеет значения для большинства применений.
nonatomic
свойства не используют блокировки, но прямой ivar быстрее, потому что он пропускает вызов accessors. Для получения подробной информации прочитайте следующее письмо от lists.apple.com.источник
Свойства по сравнению с переменными экземпляра - это компромисс, в конце концов выбор зависит от приложения.
Инкапсуляция / сокрытие информации Это хорошая вещь с точки зрения дизайна, узкие интерфейсы и минимальная связь - вот что делает программное обеспечение удобным и понятным. В Obj-C довольно сложно что-либо скрыть, но переменные экземпляра, объявленные в реализации, настолько близки, насколько это возможно.
Производительность Хотя «преждевременная оптимизация» - это плохая вещь, писать плохо работающий код только потому, что вы можете, по крайней мере, так же плохо. Трудно утверждать, что вызов метода дороже, чем загрузка или хранение, а в коде, интенсивно использующем вычислительные ресурсы, стоимость скоро возрастает.
В статическом языке со свойствами, такими как C #, вызовы к установщикам / получателям часто могут быть оптимизированы компилятором. Однако Obj-C динамичен, и удаление таких вызовов намного сложнее.
Абстракция Аргументом против переменных экземпляра в Obj-C традиционно является управление памятью. В случае, когда переменные экземпляра MRC требуют, чтобы вызовы сохраняли / освобождали / авто-релиз распространялись по всему коду, свойства (синтезированные или нет) хранят код MRC в одном месте - принцип абстракции, который является хорошей вещью (TM). Однако с GC или ARC этот аргумент пропадает, поэтому абстракция для управления памятью больше не является аргументом против переменных экземпляра.
источник
Свойства выставляют ваши переменные другим классам. Если вам просто нужна переменная, относящаяся только к классу, который вы создаете, используйте переменную экземпляра. Вот небольшой пример: классы XML для синтаксического анализа RSS и тому подобное циклически перебирают кучу методов-делегатов и тому подобное. Целесообразно иметь экземпляр NSMutableString для хранения результатов каждого прохода анализа. Нет причины, по которой внешний класс должен был бы когда-либо обращаться к этой строке или манипулировать ею. Итак, вы просто объявляете это в заголовке или в частном порядке и обращаетесь к нему по всему классу. Установка свойства для него может быть полезна только для того, чтобы убедиться в отсутствии проблем с памятью, используя self.mutableString для вызова метода получения / установки.
источник
Обратная совместимость была фактором для меня. Я не мог использовать какие-либо функции Objective C 2.0, потому что я разрабатывал программное обеспечение и драйверы принтеров, которые должны были работать на Mac OS X 10.3 как часть требования. Я знаю, что ваш вопрос, казалось, был нацелен на iOS, но я решил поделиться причинами, по которым я не использовал свойства.
источник