2011-12-14 13 views
16

Si usted tiene una propiedad en su interfaz pública como la siguiente¿Cuál es la visibilidad de las variables de instancia @synthesized?

@interface MyClass : NSObject 
@property(strong) NSString *myProp; 
@end 

Y luego sintetizarlo, en efecto, la síntesis de la variable:

@implementation MyClass 
@synthesize myProp = _myProp; // or just leave it at the default name.. 
@end 

¿Cuál es la visibilidad de la variable de instancia _myProp? Es decir, ¿esto se considera @public, @protected o @private? Supongo que, dado que MySubClass podría heredar desde MyClass, también obtendría las propiedades (naturalmente), pero ¿también heredaría la visibilidad de la variable de instancia?

¿Qué diferencia hay si pongo la propiedad en una extensión de clase? Eso escondería la propiedad de las subclases, y supongo que la variable de instancia también. ¿Está esto documentado en algún lugar?

Respuesta

28

Un ivar sintetizado es completamente invisible a todo el código que no puede ver la línea @synthesize (que básicamente significa algo fuera del archivo .m). No es @protected, no es @private, es simplemente desconocido. Con un identificador @private, a otro código que intente acceder se le informará que es privado, pero con un ivar sintetizado, a otro código que intente acceder se le informará que el campo simplemente no existe.

Como experimento mental, intente imaginar una situación en la que el ivar actuó como si fuera @protected. Usted hace una subclase, y usted ensucia con el ivar allí. Ahora regrese a la superclase y cambie @synthesize myProp a @synthesize myProp=foo. ¿Qué sucede en la subclase? Cuando el compilador procesa la subclase, no puede ver la línea @synthesize, por lo que no tendría idea de que acaba de cambiar el nombre del ivar. De hecho, ni siquiera puede decir si la propiedad está respaldada por un ivar en absoluto, o si se implementa con métodos de acceso escritos a medida. Espero que sea obvio por qué esto significa que la subclase no puede acceder al ivar, y tampoco a ninguna otra clase.

Dicho esto, no estoy muy seguro de lo que hace el compilador si escribe código en el mismo archivo .m que intenta acceder al ivar. Espero que trate el ivar como @private (ya que el compilador puede, de hecho, ver que existe el ivar).

Además, nada de esto influye en los métodos de tiempo de ejecución. Otras clases todavía pueden usar los métodos de tiempo de ejecución obj-c para buscar dinámicamente la lista de ivar de la clase y rebuscar con ella.

+6

¡Muy bien explicado! –

1

Si se declara en su interfaz, es prácticamente público cuando se utiliza el declarativo @property. Si desea utilizar los declarativos de @property y mantener su propiedad verdaderamente privada, debe crear una categoría privada en su implementación.

MyClass.h

@interface MyClass : NSObject { 
@private 
    NSObject* foo; 
} 
@end 

MyClass.m

#import "ClassWithPrivateProperty.h" 

@interface MyClass() 
    @property (nonatomic,retain) NSObject* foo; 
@end 

@implementation MyClass 
@synthesize foo; 
// class implementation... 
@end 
+1

Buen consejo, pero no dice nada sobre la visibilidad del ivar en sí. –

+1

Su respuesta realmente no aborda la visibilidad del respaldo ivar _itself_. –

1

A actúa variables sintetizadas como si se declara @private:

@interface Garble : NSObject 
@property (copy) NSString * s; 
@end 
@implementation Garble 
@synthesize s; 
@end 

@interface Bargle : Garble 
@end 

@implementation Bargle 

- (void) useS { 
    NSLog(@"%@", s); // error: instance variable 's' is private 
} 

@end 

Juro que he visto esto en the docs, pero no puedo encontrarlo ahora. Se actualizará si lo rastreo.

0

Otras clases tienen acceso a todo lo que #include. En otras palabras, a todo lo que está dentro de tu encabezado.

Si algo aparece solo en su archivo de implementación, otras clases (incluidas las subclases) no saben que existe. Una propiedad sintetizada es así. Otras clases solo conocen la propiedad (una propiedad significa un método getter y un método setter) pero no saben nada sobre la implementación interna de sus métodos.

Tenga en cuenta que los especificadores de acceso (público/privado/protegido) en obj-c son solo una pista para el compilador de que aunque aparezca algo en el archivo de encabezado, no se puede acceder. El tiempo de ejecución no lo verifica de ninguna manera.

¿Qué sucede si lo pones en una extensión de clase? Tenga en cuenta que una propiedad es un conjunto de dos métodos. Simplemente ocultas los métodos de cada clase, que incluye el encabezado principal de tu clase, pero no el encabezado de la extensión de clase.

Usamos esto por ejemplo para declarar una propiedad como de solo lectura y en la continuación de la clase lo declaramos como readwrite. Entonces, podemos usar el setter solo desde dentro de la clase.

+0

Sus primeros tres párrafos son algo inexactos; la variable sintetizada todavía no se puede acceder, incluso si está definida en el mismo archivo (ver mi publicación).Segundo, las [directivas de alcance] (http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF127) son más que simples pistas; un compiler _error_ resultará de intentar acceder a un '@ private' ivar. Tiene razón en que es posible sortear eso con las funciones de tiempo de ejecución. –

+0

Tiene razón, puede haber dos implementaciones en un archivo y luego se comporta como @private. De todos modos, la primera pista por la que no se puede acceder es porque no está incluida. – Sulthan

1

Puede crear una propiedad dinámica e indicarle al compilador que su creación de instancias sería en tiempo de ejecución.

Y a continuación, en su subclase escriba su propio captador o sintetice la propiedad.

BaseClass @interface: NSObject

@property (no atómica, fuerte) NSString * ThisWillBeSynthesizedInRespectiveSubclasses;

@end

@implementation BaseClass

ThisWillBeSynthesizedInRespectiveSubclasses @dynamic;

@end

En las clases de Sub

@interface Subclase: BaseClass

@end

@implementation Subclase @synthesize ThisWillBeSynthesizedInRespectiveSubclasses = _ThisWillBeSynthesizedInRespectiveSubclasses;

@end

o escribir sus propios métodos setter/getter.

Espero que esto ayude!

Cuestiones relacionadas