Свойства и переменные экземпляра

Я не понимаю, почему некоторые классы объявляют свойство, но не объявляют ivar и наоборот.

Является ли стандартной практикой при объявлении переменной экземпляра также объявлять ее как свойство?

Пример:

@interface AppDelegate : NSObject <UIApplicationDelegate>
{
    UIWindow *window;
    UINavigationController *navigationController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;

это просто стандартная практика при объявлении ivar класса, чтобы сделать его также свойством?

Я понимаю, что @property создает свой собственный удлиненный сеттер (и геттер с @synethesize), но почему он также должен быть ivar?


person GPP    schedule 18.08.2012    source источник


Ответы (1)


Это не так.

Раньше ивары требовалось объявлять в @interface. На самом деле это по-прежнему верно для целей PPC и i386 (т.е. 32-разрядной Intel). Это связано с проблемой хрупкого базового класса, которая требует, чтобы все подклассы знали точный размер своего суперкласс. Таким образом, ивары должны быть в @interface, иначе никто не сможет подклассировать класс.

С переходом на x86_64 и ARM, наряду с obj-c 2.0, появилось исправление проблемы хрупкого базового класса. Благодаря этому исправлению размеры классов больше не нужно знать во время компиляции, но их можно отложить до времени выполнения. Следовательно, ивары могут быть объявлены и в других местах. Примечательно, что ивар теперь может быть синтезирован из @property (точнее, из строки @synthesize в реализации). В Clang они также могут быть объявлены в блоке расширения класса (который выглядит как @interface ClassName ()) или непосредственно в файле @implementation.

Сегодня есть 3 причины, по которым вы найдете ивары, объявленные в @interface блоках:

  1. Старый код (или программисты со старыми привычками), который не был обновлен, чтобы воспользоваться возможностью скрывать объявления ivar.
  2. Код, который должен работать на PPC или i386.
  3. Код, который по какой-либо причине хочет, чтобы их ивары были общедоступными. Такого никогда не должно быть.

При написании кода сегодня, который не должен ориентироваться на старую среду выполнения, вы должны либо синтезировать свои ivars из ваших свойств (предпочтительно), либо, если вам нужны ivars, которые не привязаны к свойствам, вы должны объявить их в расширении класса или на ваш @implementation. Основная причина этого заключается в том, что файл заголовка документирует общедоступный API вашего класса и не должен содержать ничего, что не является общедоступным. Ивары не являются общедоступными, поэтому их не должно быть в заголовочном файле.

person Lily Ballard    schedule 18.08.2012
comment
Или, если вы хотите быть необычным, вы можете использовать OBJC_IVAR: stackoverflow.com/questions/11998887/ - person Richard J. Ross III; 18.08.2012
comment
Спасибо за ответ, Кевин. Вопрос: Похоже, что объявление iVar в сочетании со свойством является устаревшей методологией. Но когда я должен объявлять переменную экземпляра, а НЕ свойство? Только ради закрытия. Спасибо! - person GPP; 20.08.2012
comment
@GPP: это вопрос личных предпочтений. В общем, я бы сказал, что объекты obj-c всегда должны быть свойствами. Но для типов данных C вы можете решить, что предпочитаете просто использовать ivars напрямую. Или вы можете предпочесть использовать свойства. Это зависит от вас. Обратите внимание, что если вам нужно частное свойство, вы должны объявить его в расширении класса в вашем файле .m. - person Lily Ballard; 20.08.2012
comment
@KevinBallard, что вы имеете в виду, говоря: ивары могут быть объявлены в других местах. ? Я все еще не совсем понял, где хранятся свойства? Они все еще не ивары? Являются ли они иварами, но их смещение иваров будет рассчитываться во время выполнения, а не во время компиляции? Я хотел бы иметь более полное представление о том, что происходит!!! - person denis631; 03.05.2015
comment
@ denis631 denis631 Это означает, что компилятор может синтезировать ивары на основе реализации класса. В хрупких архитектурах переменные должны объявляться в блоке @interface, потому что все подклассы должны знать точный размер/выравнивание схемы памяти суперкласса. В современных архитектурах это уже не так, поэтому ivars больше не нужно помещать в общедоступный заголовочный файл. Доступ к иварам теперь осуществляется через отдельный символ, который содержит смещение ивара. Так что да, смещение в основном определяется во время выполнения. - person Lily Ballard; 04.05.2015
comment
@KevinBallard, что вы имеете в виду, говоря: доступ к иварам теперь осуществляется через отдельный символ? Как определяется смещение во время выполнения? Дилд так работает? Что компилятор делает со свойствами? Как насчет производительности? Конечно, время выполнения всегда медленнее, чем время компиляции, но насколько??? - person denis631; 05.05.2015
comment
@denis631 Каждый ивар получает скрытый символ формы OBJC_IVAR_$_ClassName.ivarName в разделе __DATA,__objc_ivar. Когда среда выполнения obj-c инициализирует классы, она обновляет значение каждого символа ivar со смещением, которое ivar имеет от начала класса. Каждый доступ к ivar затем переписывается, чтобы использовать этот символ для смещения вместо значения времени компиляции (хотя компилятор знает, что смещение никогда не изменится после первого доступа, поэтому он может оптимизировать избыточные нагрузки). - person Lily Ballard; 06.05.2015
comment
@KevinBallard СПАСИБО !!! Вы сказали мне именно то, что я искал. Я создал класс со свойством (code) и хотел посмотреть этот раздел. Я написал команду: otool -s __DATA __objc_ivar test.o, чтобы просмотреть содержимое этого раздела, но все, что у меня есть, это: 00 Что это за числа? Где вы нашли информацию о __DATA__objc_ivar? Я хочу больше разбираться в бинарниках Mach-O, можете ли вы мне что-нибудь посоветовать? - person denis631; 07.05.2015
comment
@ denis631 Просмотр самого раздела не очень поможет. Все, что вы действительно видите, это то, что первое значение — это 8, которое оказывается значением _OBJC_IVAR_$_TestClass._ivarString, но вы не узнаете этого, если не посмотрите адрес этого символа. Я рекомендую вам взять свой код и запустить clang -S main.m. Это создаст файл main.s, содержащий сборку для вашего файла, и вы сможете увидеть там все подробности. - person Lily Ballard; 08.05.2015