Uma @property privada cria uma variável de instância @private?

Eu li que @synthesize irá criar automaticamente variables ​​de instância correspondentes para @property e que ivars são @protected por padrão. Mas, e se eu usar uma extensão de class (como abaixo) para indicar que os methods @property devem ser privados?

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

O ivar correspondente será então @private ? Ou devo declará-lo explicitamente como @private ?

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

@private variables ​​de instância @private são um recurso somente de tempo de compilation. Dado que os ivars de apoio para @property já estão ocultos, @private não faz nada. Então, em essência, já é @private .

Elaborando a resposta de Kevin:

Quando você declara uma aula, por exemplo:

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

o compilador 1 decide sobre um layout de variável de instância para essa class. Esse layout determina os deslocamentos de variables ​​de instância em relação ao endereço de instâncias dessa class. Um layout possível seria:

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

Quando uma variável de instância é referenciada no código – na implementação da class ou em alguma outra parte da base de código, o compilador substitui essa referência pelo deslocamento correspondente da variável de instância em relação ao endereço da instância correspondente.

Por exemplo, na implementação do SomeClass,

 privateIvar = someObject; 

ou

 self->privateIvar = someValue; 

é traduzido como algo como:

 *(self + offsetOfPrivateIvar) = someObject; 

Da mesma forma, fora da class,

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

é traduzido como algo como:

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

No entanto, o compilador só permite isso de acordo com a visibilidade da variável de instância:

  • Uma variável de instância privada pode ser referenciada apenas na implementação da class correspondente;
  • Uma variável de instância protegida pode ser referenciada apenas na implementação da class correspondente e suas subclasss;
  • Uma variável de instância pública pode ser referenciada em qualquer lugar.

Quando uma variável de instância é declarada em uma extensão de class, por exemplo

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

o compilador adiciona-o ao layout da variável de instância:

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

e qualquer referência a essa variável de instância é substituída por seu deslocamento correspondente em relação à instância. No entanto, como essa variável de instância só é conhecida no arquivo de implementação no qual a extensão de class foi declarada, o compilador não permitirá que outros arquivos façam referência a ela. Um arquivo de origem arbitrário só pode referenciar variables ​​de instância que conhece (respeitando as regras de visibilidade). Se as variables ​​de instância forem declaradas em um arquivo de header que é importado por um arquivo de origem, o arquivo de origem (ou, mais precisamente, o compilador enquanto traduz essa unidade) está ciente delas.

Por outro lado, uma variável de extensão é conhecida apenas pelo arquivo de origem em que foi declarada. Assim, podemos dizer que as variables ​​de instância declaradas em extensões de class estão ocultas de outros arquivos. O mesmo raciocínio se aplica a variables ​​de instância de apoio de propriedades declaradas em extensões de class. É semelhante a @private , mas mais restritivo.

Observe, no entanto, que em regras de visibilidade de tempo de execução não são aplicadas. Usando o Codificação de valor-chave, um arquivo de origem arbitrário pode, algumas vezes (as regras são descritas aqui ), acessar uma variável de instância:

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

incluindo uma variável de instância declarada em uma extensão:

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

Independentemente do KVC, o access a variables ​​de instância pode ser feito através da API de tempo de execução do 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); 

Observe que uma turma pode ter mais de uma extensão de turma. No entanto, uma extensão de class não pode declarar uma variável de instância com o mesmo nome de outra variável de instância, incluindo variables ​​de instância declaradas em outras extensões de class. Desde que o compilador emite símbolos como:

 _OBJC_IVAR_$_SomeClass.extensionIvar 

para cada variável de instância, ter extensões diferentes declarando variables ​​de instância com o mesmo nome não produz um erro de compilador porque um determinado arquivo de origem não está ciente de outro arquivo de origem, mas produz um erro de vinculador.

1 Esse layout pode ser alterado pelo tempo de execução do Objective-C. Na verdade, os deslocamentos são calculados pelo compilador e armazenados como variables, e o tempo de execução pode alterá-los conforme necessário.

PS: Nem tudo nesta resposta se aplica a todas as versões do compilador / runtime. Eu só considerei Objective-C 2.0 com ABI não frágil e versões recentes do Clang / LLVM.