Pregunta

Este es un pequeño detalle, pero cada vez que algo de carga perezosa me quedan atrapados en ella. Son estos dos métodos aceptables? Es o mejor? Supongamos que la variable tiene la propiedad de retener.

Método # 1

(AnObject *)theObject{
    if (theObject == nil){
        theObject = [[AnObject createAnAutoreleasedObject] retain];
    }
    return theObject;
}

Método # 2

(AnObject *)theObject{
    if (theObject == nil){
        self.theObject = [AnObject createAnAutoreleasedObject];
    }
    return theObject;
}

En primer lugar, no estoy seguro de si está bien para acceder a otra función de acceso dentro de un descriptor de acceso (no veo por qué no, sin embargo). Pero parece que el establecimiento de la variable de clase sin tener que pasar a través de la incubadora podría ser igual de malo si el colocador hace algo especial (o si la propiedad se cambia a algo más que retener y el comprador no está marcada).

¿Fue útil?

Solución

Los dos son en realidad muy frágil y no en todos idénticos, dependiendo de lo que los clientes de la clase están haciendo. Haciéndolos idénticos es bastante fácil - ver abajo - pero lo que es menos frágil que es más difícil. Tal es el precio de inicialización perezosa (y por lo general trato de evitar la inicialización perezosa de esta manera, prefiriendo tratar de inicialización de subsistemas como parte de la gestión global del estado de la aplicación).

Con # 1, usted está evitando el colocador y, por lo tanto, cualquier cosa observando el cambio no va a ver el cambio. Por "observar", me refiero específicamente a la observación clave-valor (incluyendo enlaces de cacao, que utiliza MVA para actualizar la interfaz de usuario de forma automática).

Con # 2, que dará lugar a la notificación de cambio, la actualización de la interfaz de usuario y de otra manera exactamente como si el compositor fue llamado.

En ambos casos, usted tiene un potencial de recursividad infinita si la inicialización del objeto llama al comprador. Eso incluye cualquier observador si pide el valor antiguo como parte de la notificación de cambio. No hagas eso.

Si va a utilizar cualquiera de los métodos, considerar cuidadosamente las consecuencias. Uno tiene el potencial para salir de la aplicación en un estado incoherente porque un cambio de estado de una propiedad no notificó y el otro tiene el potencial de estancamiento.

Es mejor evitar el problema por completo. Véase más abajo.


Considere (recolección de basura en la herramienta de línea de comandos estándar de cacao:

#import <Foundation/Foundation.h>

@interface Foo : NSObject
{
    NSString *bar;
}
@property(nonatomic, retain) NSString *bar;
@end
@implementation Foo
- (NSString *) bar
{
    if (!bar) {
        NSLog(@"[%@ %@] lazy setting", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
        [self willChangeValueForKey: @"bar"];
        bar = @"lazy value";
        [self didChangeValueForKey: @"bar"];
    }
    return bar;
}

- (void) setBar: (NSString *) aString
{
    NSLog(@"[%@ %@] setting value %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aString);
    bar = aString;
}
@end

@interface Bar:NSObject
@end
@implementation Bar
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
    NSLog(@"[%@ %@] %@ changed\n\tchange:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), keyPath, change);
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Foo *foo = [Foo new];
    Bar *observer = [Bar new];
    CFRetain(observer);
    [foo addObserver:observer forKeyPath:@"bar"
             options: NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew
             context:NULL];
    foo.bar;
    foo.bar = @"baz";
    CFRelease(observer);

    [pool drain];
    return 0;
}

Esto no cuelgue. Es expulsado:

2010-09-15 12:29:18.377 foobar[27795:903] [Foo bar] lazy setting
2010-09-15 12:29:18.396 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    notificationIsPrior = 1;
}
2010-09-15 12:29:18.397 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    new = "lazy value";
}
2010-09-15 12:29:18.400 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    notificationIsPrior = 1;
}
2010-09-15 12:29:18.400 foobar[27795:903] [Foo setBar:] setting value baz
2010-09-15 12:29:18.401 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    new = baz;
}

Si se va a añadir NSKeyValueObservingOptionOld a la lista de opciones para la observación, que en gran medida se cuelga.

Volviendo a un comentario que hice anteriormente; la mejor solución es no hacer la inicialización perezosa como parte de su captador / definidor . Se grano demasiado fino. Estás mucho mejor gestión de su estado gráfico de objetos en un nivel superior y, como parte de eso, tienen una transición de estado que es, básicamente, del "Yo! Voy a utilizar este subsistema ahora! Cálidos que chico malo! " que hace la inicialización perezosa.

Otros consejos

Estos métodos no son idénticos. La primera de ellas es la derecha, mientras que el segundo es mal ! Un comprador no puede llamar will/didChangeValueForKey: y por lo tanto tampoco la incubadora. Esto dará lugar a infinito recursividad si se observa que la propiedad.

Y, además, no hay ningún cambio de estado para observar cuando se inicializa el miembro. Le pide a su objeto para el theObject y usted lo consigue. Cuando esto se crea es un detalle de implementación y no hay preocupación con el mundo exterior.

Si conoce el método setter propiedad es un colocador de retención estándar, son idénticos. Si no es así, tiene que decidir si otro comportamiento del colocador debe invocarse durante esa operación. Si usted no sabe, es más seguro utilizar el organismo, ya que su comportamiento puede ser importante. No se preocupe.

Ambos son básicamente idénticos, su realidad sólo depende de usted para elegir cuál es el mejor para su caso es. Ya realmente describe los pros / contras sobre el uso de la sintaxis de la propiedad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top