14: Accessors y propiedades

Introducción

Hemos visto que un objeto puede ser visible, por ejemplo una ventana o un campo de texto; o invisible, como un array o un controlador que responde a acciones del interfaz de usuario. Así que ¿qué es exactamente un objeto?

En esencia un objeto consta de una serie de valores (lo que llamamos variables) y realiza algunas acciones (lo que llamamos métodos). Un objeto contiene y transforma datos. Podemos concebir un objeto como un pequeño ordenador, que envía y responde a mensajes. Tu programa sería una especie de red en la que todos esos pequeños ordenadores trabajan juntos para producir el resultado deseado.

Composición de objetos

El trabajo de un programador Cocoa es crear clases que contengan otros objetos (como cadenas de texto, arrays y diccionarios) en los que se almacenan los valores que necesita la clase para hacer su trabajo. Algunos de esos objetos se crearán, usarán y destruirán aisladamente dentro de un método. Pero otros será necesario que permanezcan durante todo el ciclo de vida del objeto. A estos últimos objetos se les llama variables de instancia o propiedades de la clase. La clase puede también definir métodos que trabajen con estas variables.

A esta técnica se le llama composición de objetos. Estos objetos compuestos generalmente heredan directamente de NSObject.

Por ejemplo, una clase controladora de una calculadora podría contener como variables de instancia: un array de objetos botón y un campo de texto para el resultado. También podría contener métodos para sumar, restar, multiplicar y dividir números y mostrar el resultado en el GUI.

Las variables de instancia de declaran en el fichero de cabecera de la clase. En el ejemplo de nuestra calculadora sería algo así:

//[1]
@interface MiControladorDeCalculadora : NSObject {
	//Variables de instancia
	NSArray * botones;
	NSTextField * campoResultado;
}
//Métodos
- (NSNumber *)mutiplica:(NSNumber *)valor;
- (NSNumber *)suma:(NSNumber *)valor;
- (NSNumber *)resta:(NSNumber *)valor;
- (NSNumber *)divide:(NSNumber *)valor;
@end

Encapsulación

Uno de los objetivos de la programación orientada a objetos es la encapsulación: hacer cada clase lo más autocontenida y reutilizable posible. Si recordamos el capítulo 3, las variables dentro de bucles, funciones y métodos están aisladas del exterior. Este aislamiento también vale para los objetos. Esto significa que desde el exterior no podemos acceder a las variables de instancia de otros objetos, no están disponibles nada más que para los propios métodos de la clase.

Pero a veces necesitamos desde otros objetos modificar las variables de instancia de un objeto. ¿Cómo hacerlo?

Los métodos sí están disponibles desde fuera de un objeto. Todo lo que tenemos que hacer es enviar un mensaje al objeto para disparar ese método. Así que para tener disponible determinada variable crearemos un par de métodos para leer y modificar esa variable de instancia. A este par de métodos se les llama colectivamente accessors (accededores).

En el capítulo 8 descubrimos el método setIntValue: de la clase NSTextField. Este método hace pareja con el método intValue (setIntValue asigna el valor e intValue lee el valor). Juntos forman dos de los métodos accessor de NSTextField.

Accessors

¿Qué aspecto tiene esto en el código? Veamos el siguiente ejemplo.

//[2]
@interface MiPerro : NSObject {
	NSString * _nombre;   //[2.2]
}
- (NSString *)nombre;
- (void)setNombre:(NSString *)valor;
@end

Esta interfaz de la clase MiPerro tiene una variable de instancia, una cadena de texto llamada _nombre [2.2]. Para poder leer y cambiar el nombre del perro (almacenado en la variable de instancia _nombre), hemos definido dos métodos: nombre y setNombre:.

La implementación sería algo así:

//[3]
@implementation MiPerro
- (NSString *)nombre {
    return _nombre;   //[3.3]
}
- (void)setNombre:(NSString *)valor {
        _nombre = valor;   //[3.6]
}
@end

En el primer método [3.3] devolvemos simplemente la variable de instancia. En el segundo método [3.6] asignamos a la variable de instancia el valor pasado como argumento. Hemos simplificado la implementación por claridad, lo habitual es añadir código para gestión de memoria dentro de estos métodos. En el ejemplo 4 vemos un ejemplo más real:

//[4]
@implementation MiPerro
- (NSString *)nombre {
    return [[_nombre retain] autorelease];
}
- (void)setNombre:(NSString *) valor {
    if (_nombre != valor) {
        [_nombre release];
        _nombre = [valor copy];
    }
}
@end

No vamos a entrar en detalle sobre este código (ver el capítulo 15), pero podríamos decir que el código en [4] es equivalente a [3], más instrucciones para copiar, retener y liberar memoria. Las instrucciones de gestión de memoria varían según el tipo de dato. No se recomienda usar el guión delante del nombre de las variables, lo hemos puesto por claridad en nuestro ejemplo con la variable de instancia _nombre, pero podríamos haberlo llamado igual que el método (nombre) y no habría conflicto ya que variables y métodos usan espacios de nombres diferentes.

Propiedades

Con Leopard y Objective-C 2.0 aparecen nuevas características que permiten economizar cuando usamos patrones de código habituales. La nueva característica de la que estamos hablando se llama propiedades. Con ella el ahorro de código es significativo y ello implica por supuesto menos código que depurar en caso de errores.

¿En qué se diferencian las propiedades de los accessors? En esencia, las propiedades sintetizan directamente los accessors, usando la más apropiada y eficiente gestión de memoria. En otras palabras, escriben los métodos accessor por ti, aunque el código de estos métodos no queda a la vista.

El ejemplo [2] usando propiedades quedaría así:

//[5]
@interface MiPerro : NSObject {
	NSString * nombre;
}
@property (copy) NSString *nombre;
@end

Y la implementación quedaría así:

//[6]
@implementation MiPerro
@synthesize nombre;
@end

Puedes comprobar que el código se ha simplificado realmente. Si resulta que tu clase tiene muchas variables que necesitan accessor, imagínate lo que te facilita la vida el uso de propiedades.

Post new comment

The content of this field is kept private and will not be shown publicly.
Enter the code shown in the image:

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
2 + 12 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.

Donate!





If you like what you find here and wish to support further development of this site, please donate via PayPal. No account required.

Syndicate

Syndicate content

User login

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
9 + 6 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
Enter the code shown in the image: