2011-04-26 14 views
7

He leído que @synthesize will automatically create corresponding instance variables for @property y que ivars son @protected de forma predeterminada. Pero, ¿qué ocurre si utilizo una extensión de clase (como a continuación) para indicar que los métodos @property deben ser privados?¿Una propiedad @ privada crea una variable de instancia @private?

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

¿El ivar resultante será @private? ¿O debería declararlo explícitamente como @private?

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

Respuesta

12

@private Las variables de instancia son una función de solo tiempo de compilación. Dado que los ivars de respaldo para @property ya están ocultos, @private no hace nada. Entonces, en esencia, ya es @private.

+7

.... y no existe una "categoría anónima". Eso sería una "extensión de clase" en la pregunta de OP; una bestia muy diferente de una categoría (aunque similar en el aspecto de la característica). – bbum

+0

@bbum, gracias, arreglado! – ma11hew28

31

elaboro sobre del respuesta de Kevin:

Cuando se declara una clase, por ejemplo .:

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

el compilador decide sobre una disposición variable de instancia de esa clase. Este diseño determina las compensaciones de variables de instancia con respecto a la dirección de las instancias de esa clase. Una disposición posible sería:

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

Cuando una variable de instancia se hace referencia en el código - ya sea en la implementación de la clase o en alguna otra parte de la base de código, el compilador reemplaza esa referencia con el correspondiente desplazamiento de la variable de instancia con respecto a la dirección de la instancia correspondiente.

Por ejemplo, en la aplicación de SomeClass,

privateIvar = someObject; 

o

self->privateIvar = someValue; 

se traduce como algo así como:

*(self + offsetOfPrivateIvar) = someObject; 

Del mismo modo, fuera de la clase,

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

se traduce como algo así como:

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

Sin embargo, el compilador sólo permite esto de acuerdo a la visibilidad de la variable de instancia:

  • Una variable de instancia privada se puede hacer referencia sólo en la puesta en práctica de la clase correspondiente;
  • Se puede hacer referencia a una variable de instancia protegida solo en la implementación de la clase correspondiente y sus subclases;
  • Se puede hacer referencia a una variable de instancia pública en cualquier lugar.

Cuando una variable de instancia se declara en una extensión de clase, p.

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

el compilador añade a la instancia del formato de variable:

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

y cualquier referencia a esa variable de instancia se reemplaza por su desplazamiento correspondiente con respecto a la instancia. Sin embargo, dado que esa variable de instancia solo es conocida por el archivo de implementación donde se ha declarado la extensión de clase, el compilador no permitirá que otros archivos hagan referencia a ella. Un archivo fuente arbitrario solo puede hacer referencia a las variables de instancia que conoce (respetando las reglas de visibilidad). Si las variables de instancia se declaran en un archivo de cabecera importado por un archivo fuente, entonces el archivo fuente (o, más exactamente, el compilador mientras se traduce esa unidad) está al tanto de ellas.

Por otro lado, una variable de extensión solo se conoce por el archivo de origen donde se declaró. Por lo tanto, podemos decir que las variables de instancia declaradas en las extensiones de clase están ocultas de otros archivos. El mismo razonamiento se aplica a las variables de instancia de respaldo de propiedades declaradas en extensiones de clase. Es similar a @private, pero más restrictivo.

Tenga en cuenta, sin embargo, que en las reglas de visibilidad de tiempo de ejecución no se aplican. El uso de valores-clave de codificación, un archivo de fuente arbitrario puede a veces (las reglas se describen here) accede a una variable de instancia:

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

incluyendo una variable de instancia declarada en una extensión:

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

Independientemente de KVC , el acceso a las variables de instancia se logra a través de la API de Objective-C en tiempo de ejecución:

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); 

tenga en cuenta que una clase puede tener más de una clase extensi en. Sin embargo, una extensión de clase no puede declarar una variable de instancia con el mismo nombre que otra variable de instancia, incluidas las variables de instancia declaradas en otras extensiones de clase. Desde el compilador emite símbolos como:

_OBJC_IVAR_$_SomeClass.extensionIvar 

para cada variable de instancia, que tienen diferentes extensiones que declaran variables de instancia con el mismo nombre no produce un error de compilación porque un archivo fuente dada no tiene conocimiento de otro archivo fuente, pero produce un error de enlazador.

Este diseño se puede cambiar por el tiempo de ejecución de Objective-C. De hecho, el compilador calcula los desplazamientos y los almacena como variables, y el tiempo de ejecución puede cambiarlos según sea necesario.

PS: No todo en esta respuesta se aplica a todas las versiones de compilador/tiempo de ejecución. Solo he considerado Objective-C 2.0 con ABI no frágil y versiones recientes de Clang/LLVM.

+0

Aunque se desaconsejan estos tipos de comentarios: * gracias por esta respuesta tan informativa, concisa y reveladora *. – Madbreaks

Cuestiones relacionadas