7

Utilizando el método NSObject -(id)awakeAfterUsingCoder:(NSCoder *)decoder como un ejemplo, la documentación dice:¿Cuál es el nuevo patrón para liberarse con recuento automático de referencias?

permite que un objeto, después de ser decodificada, para sustituir otro objeto por sí mismo. Por ejemplo, un objeto que representa una fuente podría decodificarse, al , lanzarse y devolver un objeto existente que tenga la misma descripción de fuente que él mismo. De esta forma, los objetos redundantes pueden eliminarse .

Normalmente lo haría

[self release]; 
return substitutedObject; 

Con ARC tiene que salir de esta línea de salida. ¿No se filtraría esto? ¿O debería confiar en el objeto NSCoder para liberar el objeto original por mí? Si es así, ¿por qué tendrías que liberarte explícitamente a ti mismo con código que no sea ARC en primer lugar?

No creo self = nil es correcta a la luz de lo que la documentación del compilador dice sobre sí mismo: http://clang.llvm.org/docs/AutomaticReferenceCounting.html#misc.self

Respuesta

2

Como se señaló, no puede escribir [self release];. Además, awakeAfterUsingCoder: es no es un inicializador - no puede reasignar self.

¿No lo sabría?

Sí. Probado en el programa a continuación.

¿O debería simplemente confiar en el objeto NSCoder para liberar el objeto original para mí?

Un enfoque para evitar la fuga existe abajo - Yo no lo llamaría "el nuevo patrón", sólo el primer enfoque que vino a la mente. Se trata de una liberación explícita de self y en este caso una explícita retener del resultado:

#import <Foundation/Foundation.h> 

@interface MONBoolean : NSObject <NSCoding> 

- (id)initWithBool:(bool)pBool; 

- (bool)isTrue; 
- (bool)isFalse; 

@end 

static NSString * const MONBoolean_KEY_value = @"MONBoolean_KEY_value"; 

@implementation MONBoolean 
{ 
    bool value; 
} 

- (id)initWithBool:(bool)pBool 
{ 
    self = [super init]; 
    if (0 != self) { 
     value = pBool; 
    } 
    return self; 
} 

- (bool)isTrue 
{ 
    return true == value; 
} 

- (bool)isFalse 
{ 
    return false == value; 
} 

- (NSString *)description 
{ 
    return [[NSString alloc] initWithFormat:@"<%s:%p> : %s", object_getClassName(self), self, self.isTrue ? "true" : "false"]; 
} 

- (void)encodeWithCoder:(NSCoder *)aCoder 
{ 
    [aCoder encodeBool:value forKey:MONBoolean_KEY_value]; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    self = [super init]; 
    if (0 != self) { 
     value = [aDecoder decodeBoolForKey:MONBoolean_KEY_value]; 
    } 
    return self; 
} 

- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder 
{ 
    const bool b = value; 
    // cannot reassign self outside of an initializer. 
    // if not released, will result in a leak: 
    CFRelease((__bridge const void*)self); 
    MONBoolean * result = [[MONBoolean alloc] initWithBool:b]; 
    // now we have to retain explicitly because this is 
    // an autoreleasing method: 
    CFRetain((__bridge const void*)result); 
    return result; 
} 

@end 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     MONBoolean * a = [[MONBoolean alloc] initWithBool:true]; 
     NSData * data = [NSKeyedArchiver archivedDataWithRootObject:a]; 
     MONBoolean * b = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 
     NSLog(@"%@", b); 
    } 
    system("leaks NAME_OF_PROCESS_HERE"); 
    return 0; 
} 
+0

¿Puedes aclarar por qué tienes que retener explícitamente el resultado? Seguramente eso derrota el propósito de ARC. Gracias por responder a todo lo demás. – jamesmoschou

+0

@moshy de nada. sí, al principio pasé por alto ese detalle. la razón es que 'awakeAfterUsingCoder:' devuelve una referencia no propietaria (o autorrellenada). como tal, ARC inserta una disminución de ref-count para nosotros para nuestro valor de retorno. lo que queremos hacer es efectivamente * transferir * la referencia de un objeto a otro. Lo ejecuté en instrumentos, sin fugas. no zombies sin la retención explícita, se enviaría un mensaje a un zombi. sin la versión explícita, una fuga. pruébalo tú mismo (coloca el bloque '@ autorelease' en un' while (1) '). – justin

+1

¿Podría hacer que el método sea miembro de la familia 'init' con' __attribute __ ((objc_method_family (init))) 'y luego reasignar' self'? –

0

Creo ARC es lo suficientemente inteligente como para realizar un seguimiento de todos los objetos. Por lo tanto, no podrá decir nada acerca de la memoria y la aplicación liberará el objeto cuando ya no esté en uso. Ejecútelo a través del generador de perfiles de fugas, por las dudas, pero debería estar bien.

+4

Pero mi entendimiento es que el recuento automático de referencias es solo el recuento de referencias. No es lo mismo que la recolección de basura. Por lo tanto, todavía es posible (teóricamente) tener objetos con fugas cuando ya no están en uso. Lo ejecutaré a través del generador de perfiles. – jamesmoschou

4

Surge un problema similar en el contexto de objetos de alto nivel NIB en Mac OS X. El Resource Programming Guide dice:

Si el propietario del archivo no es una instancia de NSWindowController o NSViewController, entonces usted necesita para disminuir el número de referencia del nivel superior se opone a sí mismo. Con el recuento de referencias manual, fue posible lograr esto enviando un mensaje de release a objetos de nivel superior. No puedes hacer esto con ARC. En su lugar, emite referencias a objetos de nivel superior a un tipo de Fundación básica y usa CFRelease.

Por lo tanto, esa técnica también se puede usar presumiblemente en esta situación. CFRelease((__bridge CFTypeRef)self);

Cuestiones relacionadas