¿Cuál es la diferencia entre ivars y propiedades en Objective-C?

82

¿Cuál es la diferencia semántica entre estas 3 formas de usar ivars y propiedades en Objective-C?

1.

@class MyOtherObject; 
@interface MyObject {
}
@property (nonatomic, retain) MyOtherObject *otherObj;

2.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}
@property (nonatomic, retain) MyOtherObject *otherObj;

3.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}
ennuikiller
fuente

Respuestas:

57

El número 1 se diferencia de los otros dos al declarar hacia adelante la clase MyOtherObject para minimizar la cantidad de código visto por el compilador y el enlazador y también potencialmente evitar referencias circulares. Si lo hace de esta manera, recuerde poner el #import en el archivo .m.

Al declarar un archivo @property (y hacer coincidir @synthesize en el .m), usted genera automáticamente métodos de acceso con la semántica de memoria manejada como usted lo especifica. La regla general para la mayoría de los objetos es Retain, pero NSStrings, por ejemplo, debería usar Copiar. Mientras que los Singletons y Delegates normalmente deberían usar Assign. Los accesos de escritura a mano son tediosos y propensos a errores, por lo que esto evita muchos errores de escritura y tontos.

Además, declarar una propiedad sintetizada le permite llamar a un método de acceso usando notación de puntos como esta:

self.otherObj = someOtherNewObject; // set it  
MyOtherObject *thingee = self.otherObj; // get it 

En lugar de la forma normal de pasar mensajes:

[self setOtherObject:someOtherNewObject]; // set it
MyOtherObject *thingee = [self otherObj]; // get it 

Detrás de escena, realmente estás llamando a un método que se ve así:

- (void) setOtherObj:(MyOtherObject *)anOtherObject {

    if (otherObject == anOtherObject) {
        return;  
    }

    MyOtherObject *oldOtherObject = otherObject; // keep a reference to the old value for a second
    otherObject = [anOtherObject retain]; // put the new value in  
    [oldOtherObject release]; // let go of the old object
} // set it

…o esto

- (MyOtherObject *) otherObject {  
    return otherObject;
} // get it

Dolor total en el trasero, cierto. Ahora haz eso con cada ivar de la clase. Si no lo hace exactamente bien, obtendrá una pérdida de memoria. Es mejor dejar que el compilador haga el trabajo.

Veo que el Número 1 no tiene ivar. Suponiendo que no es un error tipográfico, está bien porque las directivas @property / @synthesize declararán un ivar para usted también, detrás de escena. Creo que esto es nuevo para Mac OS X: Snow Leopard e iOS4.

El número 3 no tiene esos accesos generados, por lo que debe escribirlos usted mismo. Si desea que sus métodos de acceso tengan efectos secundarios, haga su baile de administración de memoria estándar, como se muestra arriba, luego haga cualquier trabajo adicional que necesite, dentro del método de acceso. Si sintetiza una propiedad además de escribir la suya propia , entonces su versión tiene prioridad.

¿Cubrí todo?

willc2
fuente
¡si muchas gracias! Una nota que me gustaría hacer es que si sacas el pragma de la clase de avance en el n. ° 1 y lo reemplazas con un #import "MyOtherObject", obtienes un error de tiempo de compilación, aunque no estoy seguro de por qué ....
ennuikiller
¿Existe alguna ventaja de utilizar el enfoque n. ° 2 sobre el enfoque n. ° 1?
Greg
@Greg Método # 1 evitará una referencia circular. Ver stackoverflow.com/questions/7221174/…
willc2
3
Buena respuesta, excepto un poco sobre la notación de puntos. No es necesario sintetizar la propiedad para usarla en la notación de puntos. De hecho, no necesita declarar una propiedad en absoluto. Siempre que tenga un establecedor y un captador declarados (por ejemplo, setFoo:y foo), puede usar la notación de puntos.
JeremyP
Por relevancia, si usa ARC, la síntesis se realiza automáticamente.
Sean Larkin
17

En los viejos tiempos tenías ivars, y si querías dejar que otra clase los estableciera o los leyera, entonces tenías que definir un getter (es decir, -(NSString *)foo)y un setter (es decir, -(void)setFoo:(NSString *)aFoo;).

Lo que te dan las propiedades es el setter y getter gratis (¡casi!) Junto con un ivar. Entonces, cuando defina una propiedad ahora, puede establecer la atomicidad (¿desea permitir múltiples acciones de configuración de múltiples subprocesos, por ejemplo), así como asignar / retener / copiar semántica (es decir, si el establecedor copia el nuevo valor o simplemente guarde el valor actual - importante si otra clase está intentando establecer su propiedad de cadena con una cadena mutable que podría cambiarse más adelante).

Esto es lo que @synthesizehace. Muchas personas dejan el nombre de ivar igual, pero puede cambiarlo cuando escribe su declaración de síntesis (es decir, @synthesize foo=_foo;significa hacer un ivar con el nombre _foode la propiedad foo, por lo que si desea leer o escribir esta propiedad y no la usa self.foo, lo hará tiene que usar _foo = ..., solo le ayuda a captar referencias directas al ivar si solo desea pasar por el setter y getter).

A partir de Xcode 4.6, no es necesario utilizar la @synthesizedeclaración: el compilador lo hará automáticamente y, de forma predeterminada, antepondrá el nombre del ivar con _.

David H
fuente
1
Cabe señalar que la atomicidad de una propiedad no garantiza la seguridad de los subprocesos .
jscs
Entonces, si tengo un ivar que es atómico, ¿quiere decir que mientras el establecedor lo está configurando o el captador lo está obteniendo, otro hilo se activa e intenta hacer cualquiera de las dos, que todo se frustra? Entonces, ¿cuál es el punto de atómico? Tengo entendido que atómico al menos se asegura de que si establece un ivar, se establece, su recuento de retención es apropiado, etc. [No es que resuelva todos los problemas, solo evita que te engañen]
David H
2
Tiene la garantía de obtener un objeto completo válido (el captador no devolverá un objeto que está en proceso de ser desasignado), pero si otro subproceso está usando el configurador, puede obtener el valor antes o después. Especificar que debe manejarse fuera de los captadores y definidores. En otras palabras, no se interrumpirá ningún hilo durante la operación getter o setter, pero el orden de las operaciones no está (no puede ser, en este nivel, AFAIK) definido.
jscs
Bueno, yo diría que su comentario original estaba fuera de lugar: se respeta la atomicidad, es solo que el acceso a través de subprocesos puede generar una serie de problemas, por lo tanto, cada ivar que he declarado es atómico y si hay subprocesos involucrados, entonces concurrencia se trata en otro lugar.
David H