2011-12-20 20 views
6

He leído las preguntas ya publicadas extensamente y no puedo encontrar la respuesta que estoy buscando.Objetivo C - Accessors ie Getters/Setters

entiendo totalmente el concepto de usar la directiva @syntesize para crear métodos getter y setter (es decir, si tuviera @property int width y @synthesize width, estoy inadvertidamente creando un método de obtención de width y un método de selección de setWidth:).

Sin embargo, cuando no estoy usando la directiva @synthesize pero declaro variables de instancia en la sección @implementation que son objetos, no entiendo completamente cómo funcionan los métodos de acceso. Esto es lo que no entiendo de la siguiente código:

1) en main donde dice:

NSLog(@"Origin at (%i, %i)", myRect.origin1.x, myRect.origin1.y); 

Me parece como si fuera a estar llamando el método [[myRect origin1] x] que primero determinar que [myRect origin1] devuelve origin y luego llamará inmediatamente al [origin x] como resultado (y luego haga lo mismo para y). Ahora, lo que me arroja es el hecho de que si tuviera que cambiar el nombre del método getter

-(XYpoint *) origin1; 

contenido dentro Rectangle.h a

-(XYpoint *) origin2; 

el programa recibe montones de errores y deja de compilar. Nota: También ha cambiado el nombre de este método en todas partes se hace referencia incluyendo el cambio del código anterior en el principal a

NSLog(@"Origin at (%i, %i)", myRect.origin2.x, myRect.origin2.y); 

Sin embargo, si yo también cambiar el nombre del método de selección de:

-(void) setOrigin1: (XYpoint *) pt 

a :

-(void) setOrigin2: (XYpoint *) pt 

todo funciona como antes. Me parece que solo funciona correctamente cuando mi getter y setter son nombrados en la convención de nomenclatura xsetX. Supongo que esto es principalmente lo que necesito explicar:

A) Si creo una variable de instancia que resulta ser un objeto (como 'origen' en este caso), ¿debo crear métodos getter y setter para ello?

B) ¿Puedo crear un método getter pero no es un método de selección o viceversa

C) ¿Es obligatorio que si hago crear tanto un captador y el método de selección de 'origen' que ambos se nombran en la manera xsetX. En este caso como -(XYpoint *) origin1 y -(void) setOrigin1: (XYpoint *) pt. Como en el caso de que cambie el nombre del getter, ¿debo cambiar el nombre del setter en consecuencia?

Aquí es todo el código:

Rectangle.h:

#import <Foundation/Foundation.h> 
@class XYpoint; 

@interface Rectangle : NSObject 

@property int width, height; 

-(XYpoint *) origin1; 
-(void) setOrigin1: (XYpoint *) pt; 
-(void) setWidth: (int) w andHeight: (int) h; 
-(int) area; 
-(int) perimeter; 

@end 

rectángulo.m:

#import "Rectangle.h" 

@implementation Rectangle 
{ 
    XYpoint *origin; 
} 

@synthesize width, height; 

-(void) setWidth:(int) w andHeight:(int)h 
{ 
    width = w; 
    height = h; 
} 


-(void) setOrigin1: (XYpoint *) pt 
{ 
    origin = pt; 
} 

-(int) area 
{ 
    return width * height; 
} 

-(int) perimeter 
{ 
    return (width + height) * 2; 
} 

-(XYpoint *) origin1 
{ 
    return origin; 
} 

@end 

XYpoint.h:

#import <Foundation/Foundation.h> 

@interface XYpoint : NSObject 

@property int x, y; 

-(void) setX: (int) xVal andY: (int) yVal; 
@end 

XYpoint.m:

#import "XYpoint.h" 

@implementation XYpoint 

@synthesize x,y; 

-(void) setX: (int) xVal andY: (int) yVal 
{ 
    x = xVal; 
    y = yVal; 
} 
@end 

main.m:

#import <Foundation/Foundation.h> 
#import "Rectangle.h" 
#import "XYpoint.h" 

int main (int argc, const char * argv[]) 
{ 

    @autoreleasepool { 
     Rectangle *myRect = [[Rectangle alloc] init]; 
     XYpoint *myPoint = [[XYpoint alloc] init]; 

     [myPoint setX: 100 andY: 200]; 
     [myRect setWidth: 5 andHeight:8]; 
     myRect.origin1 = myPoint; 
     NSLog(@"Rectangle w = %i, h = %i", myRect.width, myRect.height); 
     NSLog(@"Origin at (%i, %i)", myRect.origin1.x, myRect.origin1.y); 
     NSLog(@"Area = %i, Perimeter = %i", [myRect area], [myRect perimeter]); 
    } 
    return 0; 
} 
+0

La notación de puntos es en realidad solo una abreviatura de las llamadas de acceso. Dado que myRect.origin2 podría equivaler a [myRect origin2] O [myRect setOrigin2], según el contexto, ambos accesores deben definirse para poder utilizar la notación de puntos. De todos modos, deberías declarar las variables de instancia como propiedades, como en la respuesta de Jakob. – spwert

+0

¡Gracias! Ese es exactamente mi problema. Cambiar mi getter (o setter) de setOrigin1 a setOrigin2 significaría que cuando se llama myRect.origin1, esencialmente necesita un getter/setter nombrado en consecuencia. Y desde que cambié setOrigin1 a setOrigin2, ya no hay tanto getter como setter. Incluso si nunca lo uso, el compilador lo requiere. Al menos creo que eso es lo que quieres decir. Si es así, tiene sentido para mí ahora. Aunque no sé cómo darle una respuesta "correcta" ya que solo aparece como un comentario – ReiAndCoke

+0

Esto está mal. No necesita ambos accesorios. Si define '- (int) foo;' en su archivo de encabezado, puede usar 'int x = myObj.foo;'. Por otro lado, si define '- (void) setBar: (int) anInt;' en el archivo de encabezado, puede usar 'myObj.bar = 15;'. Getters y setters son independientes. –

Respuesta

3

Después de la discusión por correo electrónico, descubrimos que el problema parece ser un error en clang. Considere el siguiente programa de mini:

#import <Foundation/Foundation.h> 

@interface TestObject : NSObject 
-(void)setIdVar:(id)someId; 
@end 

@implementation TestObject 
-(void)setIdVar:(id)someId; 
{ 
    NSLog(@"-setIdVar called with argument: %@", someId); 
} 
@end 

int main (int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     TestObject *testObj = [[TestObject alloc] init]; 
     testObj.idVar = @"test"; 
    } 
    return 0; 
} 

Obviamente, esperamos que este programa se ejecute y la salida -setIdVar called with argument: test. Y eso es exactamente lo que sucede cuando compilas sin ARC (por ejemplo, usando clang -framework Foundation main.m).

Pero si lo compilamos con ARC, choca clang. (clang -framework Foundation -fobjc-arc main.m)

Lo curioso es que este bloqueo no ocurre cuando se usan setters para tipos que no son objetos (por ejemplo, int) o cuando se define un getter.

3

Lo más probable olvidó cambiar el método nombres en el encabezado o archivos de implementación. Es perfectamente válido tener propiedades de solo lectura (sin métodos setter).

La mejor práctica si tiene una propiedad de objeto que desea acceder utilizando la notación de puntos (es decir, myRect.origin1), es asegurarse de definir la propiedad correspondiente en el archivo de encabezado, es decir. incluir una línea como:

@property(readonly) XYPoint *origin1; // for read only properties 
@property(retain) XYPoint *origin1; // for read/write properties 

utilizarlos aunque no utiliza @synthesize, y usarlos en lugar de las declaraciones de métodos normales en el archivo de cabecera. Estas líneas en realidad no crean getters y setters, solo informan al compilador que su clase tiene estas propiedades. El compilador esperará getters (y setters si no usa readonly) llamados -origin1 y -setOrigin1. Los nombres de los setters/getters son importantes (consulte la Documentación de Apple sobre codificación de clave y valor para obtener más información)

También debe tener en cuenta las pautas de administración de memoria de Cocoa: a menos que utilice el recuento automático de referencias, su clase Rectangle es responsable de retener o copiar el objeto XYPoint en el colocador. [EDITAR]: Me acabo de dar cuenta de que obviamente está usando ARC ya que usa la sintaxis @autoreleasepool.

+0

He cambiado los nombres en el archivo de encabezado y en el archivo de implementación. Sin embargo, cuando mis métodos getter y setter no se nombran manualmente como "origin1" y "setOrigin1" sino como "origin1" y "setOrigin2", recibo muchísimos errores. Pero cuando cambio "setOrigin2" a "setOrigin1" todo vuelve a funcionar ... lo siento si estoy un poco confundido, soy nuevo aquí. – ReiAndCoke

+0

Esto es realmente lo que me desconcierta: ¿Por qué mi programa obtiene todo tipo de errores si cambio el nombre del método setOrigin1 setter (y también cambio su nombre en cualquier otro lugar al que se haga referencia/use). Para mí, parece que está recibiendo errores ya que ya no se nombra junto con el método de obtención de origin1. – ReiAndCoke

+0

Realmente me suena a un error tipográfico en alguna parte. Sin ver las advertencias exactas y los archivos fuente, es difícil analizar qué salió mal. El problema con el uso de la notación de punto para acceder a las propiedades es que el compilador debe estar absolutamente seguro de qué nombre tienen los getters/setters. La mejor manera de garantizar esto es declarar estas propiedades en el archivo de encabezado con la sintaxis '@property id foo;' en lugar de declarar los métodos '- (id) foo;' y '- (void) setFoo: (id) aFoo ; 'y esperando que el compilador descubra que estas son propiedades. –

5

A) Si se crea una variable de instancia que pasa a ser un objeto (como 'origen' en este caso) he de crear métodos getter y setter para ello?

No. Si declara una propiedad, deberá proporcionar sus propios descriptores de acceso o utilizar la directiva @synthesize para crearlos. Pero puede tener todas las variables de instancia que desee sin tener acceso para ellas.

B) ¿Puedo crear un método getter pero no es un método de selección o viceversa

Sí, puede proporcionar sólo el captador si se declara su propiedad readonly.

C) ¿Es obligatorio que si hago crear tanto un captador y el método de selección de 'origen' que ambos se nombran en los x setX manera. En este caso como - (XYpoint *) origin1 y - (void) setOrigin1: (XYpoint *) pt. Como en caso de que cambie el nombre del getter, ¿debo cambiar el nombre del setter en consecuencia?

Puede proporcionar sus propios nombres para los descriptores de acceso, pero debe seguir con la convención habitual si desea que su clase sea valor de la clave de codificación compatible para la propiedad en cuestión:

@property (getter=isBar, setter=setBar) int bar;