Создает ли приватный @property переменную экземпляра @private? - PullRequest
7 голосов
/ 26 апреля 2011

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

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

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

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

Ответы [ 2 ]

31 голосов
/ 26 апреля 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;

или

self->privateIvar = someValue;

переводитсякак что-то вроде:

*(self + offsetOfPrivateIvar) = someObject;

Аналогично, за пределами класса

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

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

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

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

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

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

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

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

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

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

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

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

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.

12 голосов
/ 26 апреля 2011

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

...