ARC significa que el compilador se encarga de la gestión de memoria, no ARC significa que cuidar de ella, pero en ambos casos la gestión de memoria funciona exactamente de la misma manera:
- Si un objeto debe mantenerse con vida, su retener el contador se incrementa (eso es lo que
retain
hace)
- Si un objeto ya no es necesaria, su retener contador se disminuye antes de la mención de que se pierda (eso es lo que
release
hace)
- Si ha terminado con un objeto, sino no debe morir todavía, por ejemplo como necesita devolverlo como resultado del método (y no desea devolver un objeto muerto), se debe agregar a un grupo de autorrelease que disminuirá su recuento retenido en un momento posterior (eso es lo que
autorelease
hace, es como diciendo "llame al release
sobre ese objeto en el futuro.")
- Los objetos recién creados tienen un conteo retenido de
1
.
- Si el conteo de retención llega a cero, el objeto se libera.
Si lo hace usted o el compilador lo hace por usted, no juega ningún papel. Después de la compilación, se están llamando a estos métodos, también con ARC, pero con ARC el compilador ha decidido por usted cuándo se llama el método. Hay algo de magia extra, p. ARC no siempre tiene que agregar objetos a los grupos de liberación automática cuando los devuelve como resultado del método, esto a menudo puede optimizarse, pero no tiene que importar, ya que esta magia solo se aplica si el llamante y el método llamado ambos están utilizando ARCO; si uno de ellos no lo es, entonces se usa un autorelease
normal (que todavía funciona en ARC exactamente como solía hacerlo).
Lo único que debe cuidar es retener los ciclos. Ya sea que use ARC o no, el recuento de referencias no puede tratar con ciclos de retención. No hay diferencia aquí
Pitfalls? Cuidado con los enlaces gratuitos. Un NSString *
y un CFStringRef
son de hecho lo mismo, pero ARC no conoce el mundo CF, por lo tanto, mientras ARC se ocupa del NSString
, debe ocuparse del CFString
. Cuando usa ARC, necesita decirle a ARC cómo hacer un puente.
CFStringRef cfstr = ...;
NSString * nsstr = (__bridge_transfer NSString *)cfstr;
// NSString * nsstr = [(NSString *)cfstr autorelease];
Código anterior significa "ARC, por favor asegúrese de que la propiedad de CFString
objeto y cuidar de soltarlo tan pronto como haya terminado con él". El código se comporta como el código que se muestra en el comentario a continuación; así que cuidado, cfstr
debe tener un conteo retenido de al menos uno y ARC lo lanzará al menos una vez, solo que todavía no. A la inversa:
NSString * nsstr = ...;
CFStringRef cfstr = (__bridge_retained CFStringRef)cftr;
// CFStringRef cfstr = (CFStringRef)[nsstr retain];
Código anterior significa "ARC, por favor, dame la propiedad de que NSString
, yo me encargo de liberar una vez que he terminado con ella". ¡Por supuesto, debes mantener esa promesa! En algún momento deberá llamar al CFRelease(cfstr)
, de lo contrario, perderá memoria.
Finalmente está (__bridge ...)
que es solo un tipo de molde, no se transfiere la propiedad. Este tipo de lanzamiento es peligroso, ya que puede crear punteros colgantes si intentas mantener el resultado del lanzamiento. Usualmente lo usas cuando estás alimentando un objeto ARC a una función esperando un objeto CF ya que ARC seguramente mantendrá el objeto vivo hasta que la función regrese, p.esto es siempre seguro:
doSomethingWithString((__bridge CFStringRef)nsstr);
Incluso si ARC se le permitió liberar nsstr
en cualquier momento, no hay código debajo de esa línea cada vez acceda a ella nunca más, que sin duda lo suelte antes de que esta función ha vuelto y argumentos de la función son por definición solo garantiza que permanecerá vivo hasta que la función regrese (en caso de que la función quiera mantener la cuerda viva, debe retenerla y luego ARC no la desasignará después de soltarla ya que el conteo de retención no será cero).
Lo mayoría de las personas parecen tener dificultades con que se pasa ARC objetos como void *
contexto, ya que a veces tiene que con la API de más edad, pero que en realidad es absolutamente simple:
- (void)doIt {
NSDictionary myCallbackContext = ...;
[obj doSomethingWithCallbackSelector:@selector(iAmDone:)
context:(__bridge_retained void *)myCallbackContext
];
// Bridge cast above makes sure that ARC won't kill
// myCallbackContext prior to returning from this method.
// Think of:
// [obj doSomethingWithCallbackSelector:@selector(iAmDone:)
// context:(void *)[myCallbackContext retain]
// ];
}
// ...
- (void)iAmDone:(void *)context {
NSDictionary * contextDict = (__bridge_transfer NSDictionary *)context;
// Use contextDict as you you like, ARC will release it
// prior to returning from this method. Think of:
// NSDictionary * contextDict = [(NSDictionary *)context autorelease];
}
Y tengo que realmente grande tengo para ti que no son tan obvios a primera vista. Tenga en cuenta este código:
@implementation SomeObject {
id _someIVAR;
}
- (void)someMethod {
id someValue = ...;
_someIVAR = someValue;
}
Este código no es el mismo en ARC y no en ARC. En ARC todas las variables son fuertes por defecto, por lo que en ARC este código se comporta igual que el código tendría:
@interface SomeObject
@property (retain,nonatomic) id someIVAR;
@end
@implementation SomeObject
- (void)someMethod {
id someValue = ...;
self.someIVAR = someValue;
}
Asignación someValue
lo conservará, el objeto se mantiene vivo! En la no-ARC el código se comportará como éste:
@interface SomeObject
@property (assign,nonatomic) id someIVAR;
@end
@implementation SomeObject
- (void)someMethod {
id someValue = ...;
self.someIVAR = someValue;
}
Nota la propiedad es diferente, ya que de Ivar en no-ARC no son ni strong
o weak
, no son nada, son sólo punteros (en arco que es llamado __unsafe_unretained
y la palabra clave aquí es insegura).
Si tiene un código que usa ivars directamente y no usa propiedades con setters/getters para acceder a ellos, entonces cambiar de no ARC a ARC puede provocar que se retengan los ciclos en el código que solía tener una administración de memoria sana. Por otro lado, al pasar de ARC a no ARC, un código como este puede causar punteros colgantes (punteros a objetos anteriores pero dado que el objeto ya ha muerto, estos apuntan a ninguna parte y usarlos tiene resultados impredecibles), como objetos que solían mantenerse vivo antes de que ahora pueda morir inesperadamente.
este parece ser el consenso. Gracias por tu ayuda. – TomSwift