Создает ли частное @property переменную экземпляра @private?

Я читал, что @synthesize автоматически создаст соответствующий переменные экземпляра для @property, а переменные @protected по умолчанию. Но что, если я использую расширение класса (как показано ниже), чтобы указать, что методы @property должны быть закрытыми?

// Photo.m
@interface Photo ()
@property (nonatomic, retain) NSMutableData *urlData;
@end

Будет ли тогда соответствующий ивар @private? Или я должен явно объявить это как @private вот так?

// Photo.h
@interface Photo : Resource {
@private
    NSMutableData *urlData;
}

person ma11hew28    schedule 26.04.2011    source источник


Ответы (2)


@private переменные экземпляра доступны только во время компиляции. Учитывая, что поддерживающие ivars для @property уже скрыты, @private ничего не делает. Так что по сути это уже @private.

person Lily Ballard    schedule 26.04.2011
comment
.... и нет такой вещи, как анонимная категория. Это было бы расширением класса в вопросе OP; совершенно другой зверь, чем категория (хотя и похожие по характеру). - person bbum; 26.04.2011

Разрабатывая ответ Кевина:

Когда вы объявляете класс, например:

@interface SomeClass : NSObject {
@public
    id publicIvar;
@protected
    id protectedIvar;
@private
    id privateIvar;
}
@end

компилятор1 выбирает макет переменной экземпляра для этого класса. Этот макет определяет смещения переменных экземпляра относительно адреса экземпляров этого класса. Один из возможных макетов:

        +--> publicIvar address = instance address + offsetOfPublicIvar
        |
        |
+-----+------------+-----+---------------+-----+-------------+-----+
| ... | publicIvar | ... | protectedIvar | ... | privateIvar | ... |
+-----+------------+-----+---------------+-----+-------------+-----+
|
|
+--> instance address

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

Например, в реализации SomeClass,

privateIvar = someObject;

or

self->privateIvar = someValue;

переводится примерно так:

*(self + offsetOfPrivateIvar) = someObject;

Точно так же вне класса,

SomeClass *obj = [SomeClass new];
obj->publicIvar = someObject;

переводится примерно так:

SomeClass *obj = [SomeClass new];
*(obj + offsetOfPublicIvar) = someObject;

Однако компилятор разрешает это только в зависимости от видимости переменной экземпляра:

  • На приватную переменную экземпляра можно ссылаться только в реализации соответствующего класса;
  • На защищенную переменную экземпляра можно ссылаться только в реализации соответствующего класса и его подклассов;
  • На общедоступную переменную экземпляра можно ссылаться где угодно.

Когда переменная экземпляра объявлена ​​в расширении класса, например.

@interface SomeClass () {
    id extensionIvar;
}
@end

компилятор добавляет его в макет переменной экземпляра:

+-----+------------+-----+---------------+
| ... | otherIvars | ... | extensionIvar |
+-----+------------+-----+---------------+

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

С другой стороны, переменная расширения известна только в исходном файле, где она была объявлена. Таким образом, мы можем сказать, что переменные экземпляра, объявленные в расширениях класса, скрыты от других файлов. Те же рассуждения применимы к резервным переменным экземпляра свойств, объявленных в расширениях класса. Он похож на @private, но более строгий.

Обратите внимание, однако, что правила видимости во время выполнения не применяются. Используя Key-Value Coding, произвольный исходный файл может иногда (правила описаны здесь) доступ к переменной экземпляра:

SomeClass *obj = [SomeClass new];
id privateValue = [obj valueForKey:@"privateIvar"];

включая переменную экземпляра, объявленную в расширении:

id extensionValue = [obj valueForKey:@"extensionIvar"];

Независимо от KVC, доступ к переменным экземпляра осуществляется через API среды выполнения Objective-C:

Ivar privateIvar = class_getInstanceVariable([SomeClass class],
                                             "privateIvar");
Ivar extensionIvar = class_getInstanceVariable([SomeClass class],
                                               "extensionIvar");

id privateValue = object_getIvar(obj, privateIvar);
id extensionValue = object_getIvar(obj, extensionIvar);

Обратите внимание, что класс может иметь более одного расширения класса. Однако одно расширение класса не может объявлять переменную экземпляра с тем же именем, что и другая переменная экземпляра, включая переменные экземпляра, объявленные в других расширениях класса. Поскольку компилятор выдает такие символы, как:

_OBJC_IVAR_$_SomeClass.extensionIvar

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

1Этот макет может быть изменен средой выполнения Objective-C. На самом деле смещения вычисляются компилятором и сохраняются как переменные, и среда выполнения может изменить их по мере необходимости.

PS: Не все в этом ответе применимо ко всем версиям компилятора/среды выполнения. Я рассматривал только Objective-C 2.0 с нехрупким ABI и последними версиями Clang/LLVM.

person Community    schedule 26.04.2011
comment
Хотя такие комментарии не приветствуются: спасибо за этот очень информативный, краткий и поучительный ответ. - person Madbreaks; 10.01.2013