Cuándo anular los obturadores c objectives

En el último año, he estado trabajando con otras personas en algunos proyectos de Objective-C por primera vez.

De vez en cuando (y cada vez más) veo a otras personas anulando los methods getter / accessor, AND que contienen el código de implementación en este método. Para mí, esta es una ciudad loca, ya que este es el punto de tener un setter … también significa que la propiedad que se establece en el setter será anulada en el getter, y por lo tanto, no tiene sentido.

¿Estas personas se comportan mal, o soy yo quien está perdiendo algo? ¿Hay NUNCA una necesidad de anular el método getter de una propiedad sintetizada?

Ejemplo:

@synthesize width; - (CGFloat)width { NSNumber *userWidth = [[NSUserDefaults standardUserDefaults] objectForKey:@"USER_WIDTH"]; if (userWidth != nil) { width = [NSNumber floatValue]; } //Shouldn't the above lines be done in the SETTER? I have SET the property! //Why would I ever need to write anything BUT the below line?? return width; } - (void)setWidth:(CGFloat)newWidth { //THIS is where you do the the setting! width = newWidth; } 

ACTUALIZAR:

Ok ancho es un mal ejemplo. Demasiadas personas están atrapadas en la semántica de "lo que es la variable" y "no incluyen get accesores objective-c". Así que he actualizado el ejemplo anterior para ignorar la semántica irrelevante y concentrarme en el concepto. El concepto es … ¿hay algún ejemplo cuando quiera anular el GETTER (no es setter, getter solamente. Anulo el setter muchas veces, esta pregunta es sobre getter)?

Devolver otra propiedad como capa (como se menciona a continuación) es un ejemplo genuino. Pero más específicamente, ¿existe alguna vez la necesidad de ESTABLECER la propiedad en un GETTER ? Esta es una de las rarezas que estoy viendo, así que he actualizado el getter anterior para get un valor de NSUserDefaults para ayudar a mi punto …

El primer problema es que no quieres usar getWidth. El patrón en objC es nombre y setName. No uses getName. Arruina el enlace y KVO.

Además, si solo está configurando / obteniendo el iVar, no hay motivo para anularlo. Si está haciendo un procesamiento / validation adicional, entonces puede ser correcto anular.

EDITAR:

También debe tratar de evitar la configuration de datos y el procesamiento pesado en el getter. Se supone que un captador encapsula algún estado y devuelve datos. La expectativa es que es muy liviano. El procesamiento pesado y / o las modificaciones deben hacerse en methods o establecedores. Por ejemplo, la gente establece relojes de debugging en getters y no espera un procesamiento pesado y modificación de estado.

Primero, las convenciones de nomenclatura de cocoa llamarían al getter -width , no -getWidth . "Get" se usa para completar arguments pasados:

 - (void) getWidth:(CGFloat *)outWidth { if (outWidth) *outWidth = _width; } 

Dicho eso, vuelve a tu pregunta original:

En los viejos times, antes de @property y @synthesize , tendríamos que escribir nuestros @synthesize manualmente como hicimos anteriormente.

Hay otras ocasiones en las que desea escribir manualmente un acceso, sin embargo.

Una común es retrasar la initialization hasta que se necesite un valor. Por ejemplo, diga que hay una image que tarda un time en generar. Cada vez que modifica una propiedad que alteraría la image, no desea volver a dibujar la image de inmediato. En cambio, puedes aplazar el sorteo hasta la próxima vez que alguien pregunte:

 - (UIImage *) imageThatTakesAwhileToGenerate { if (!_imageThatTakesAwhileToGenerate) { // set it here } return _imageThatTakesAwhileToGenerate; } - (void) setColorOfImage:(UIColor *)color { if (_color != color) { [_color release]; _color = [color retain]; // Invalidate _imageThatTakesAwhileToGenerate, we will recreate it the next time that the accessor is called [_imageThatTakesAwhileToGenerate release]; _imageThatTakesAwhileToGenerate = nil; } } 

Otro uso es reenviar la implementación del accessor / mutator a otra class. Por ejemplo, UIView reenvía muchas de sus properties al respaldo de CALayer :

 // Not actual UIKit implementation, but similar: - (CGRect) bounds { return [[self layer] bounds]; } - (void) setBounds:(CGRect)bounds { [[self layer] setBounds:bounds]; } - (void) setHidden:(BOOL)hidden { [[self layer] setHidden:hidden]; } - (BOOL) isHidden { return [[self layer] isHidden]; } - (void) setClipsToBounds:(BOOL)clipsToBounds { [[self layer] setMasksToBounds:clipsToBounds]; } - (BOOL) clipsToBounds { return [[self layer] masksToBounds]; } 

Actualización a la actualización del solicitante:

En su actualización, parece que el código en cuestión está tratando de mantener el valor del ancho usando NSUserDefaults, o está tratando de permitir que los usuarios especifiquen un valor personalizado para anular todos los anchos devueltos. Si este último, su ejemplo está bien (aunque limitaría esta práctica, ya que podría causar confusión a los recién llegados).

Si el primero, quiere cargar el valor de NSUserDefaults una vez, y save un nuevo valor de nuevo en NSUserDefaults en el setter. Por ejemplo:

 static NSString * const sUserWidthKey = @"USER_WIDTH"; @implementation Foo { CGFloat _width; BOOL _isWidthIvarTheSameAsTruthValue; } @synthesize width = _width; - (CGFloat) width { if (!_isWidthIvarTheSameAsTruthValue) { NSNumber *userWidth = [[NSUserDefaults standardUserDefaults] objectForKey:sUserWidthKey]; if (userWidth != nil) _width = [NSNumber doubleValue]; _isWidthIvarTheSameAsTruthValue = YES; } return _width; } - (void) setWidth:(CGFloat)newWidth { if (_width != newWidth) { _width = newWidth; NSNumber *userWidthNumber = [NSNumber numberWithDouble:_width]; [[NSUserDefaults standardUserDefaults] setObject:userWidthNumber forKey:sUserWidthKey]; _isWidthIvarTheSameAsTruthValue = YES; } } @end 

El _width ivar se usa como caching. La verdad se almacena en NSUserDefaults.

Nota: Estoy usando NSUserDefaults en este ejemplo ya que lo usaste en el tuyo. En la práctica, prefiero no mezclar NSUserDefault con mis accesores;)

¿Qué tal el caso cuando creas tu object de propiedad perezosamente? Uso este patrón muy a menudo, también se utiliza en la plantilla CoreData de Xcode, etc.

 - (NSString *)string { if (!_string) { // Create the string property lazily // Create is using some other internal, etc values _string = [NSString alloc] initWith...] } return _string; } 

También:

 - (void)setString:(NSString *)string { if (![string isEqualToString:_string]) { // Probably you want to make your property observable here too :) [_setString release]; _setString = [string retain]; // Update other things that depend on _string for example networkingraw the view, etc [self setNeedsDisplay]; } } 

Hay muchos motivos para anular tanto los methods getter como los setter, por ejemplo, anulo los methods setter cuando hago objects UITableViewCell personalizados, para que pueda establecer una propiedad y, luego, dentro de ese método setter, actualizará automáticamente una label o algo dentro de la celda, en lugar de tener que llamar a una function de actualización después de configurar la propiedad.

Es posible que desee sobrescribir un getter si desea almacenar información de manera diferente a la que recibe, un ejemplo podría ser un object de número de teléfono, donde puede almacenarlo como 5551234567 y se recuperará automáticamente como 555-123-4567 o algo así . Raramente anulo los methods getter yo mismo, pero anulo los methods setter con bastante frecuencia.

Esta es una práctica perfectamente aceptable en progtwigción orientada a objects. Sin embargo, uno debe ser consciente de los efectos secundarios. No debe hacer algo como acceso a la networking en un método setter, por ejemplo.

Sin embargo, en el código que enumera anteriormente, ya que no hacen nada diferente de lo que hacen los methods sintetizados, no hay razón para include las implementaciones. Simplemente desorderan el código.