objective c - revista - ¿Por qué ARC retiene los argumentos del método?
revista ethos etica (4)
Incluso la respuesta del alma es correcta, es un poco más profunda de lo que debería ser:
Se conserva, porque la referencia pasada se asigna a una variable fuerte, la variable de parámetro. Este y solo este es el motivo del par de retención / liberación. (Establezca el parámetro var en __weak y ¿qué sucede?)
Uno podría optimizarlo lejos? Sería como optimizar cada par de retención / liberación en las variables locales de distancia, porque los parámetros son variables locales. Esto se puede hacer, si el compilador comprende el código de agujero dentro del método, incluidos todos los mensajes enviados y las llamadas de funciones. Esto se puede aplicar que raramente ese clang ni siquiera intenta hacerlo. (Imagine que el argumento apunta a una persona (solo) que pertenece a un grupo y que el grupo está desasignado: la persona también sería desasignada).
Y sí, no retener args en MRC era un tipo de peligro, pero los desarrolladores generalmente conocen su código tan bien, que optimizan la retención / liberación sin pensar en ello.
Cuando se compila con ARC, los argumentos de los métodos a menudo parecen mantenerse al principio del método y liberarse al final. Este par de retener / liberar parece superfluo, y contradice la idea de que ARC "produce el código que habría escrito de todos modos". Nadie en esos oscuros días previos al ARC realizó una retención / liberación adicional en todos los argumentos de los métodos solo para estar seguro, ¿no?
Considerar:
@interface Test : NSObject
@end
@implementation Test
- (void)testARC:(NSString *)s
{
[s length]; // no extra retain/release here.
}
- (void)testARC2:(NSString *)s
{
// ARC inserts [s retain]
[s length];
[s length];
// ARC inserts [s release]
}
- (void)testARC3:(__unsafe_unretained NSString *)s
{
// no retain -- we used __unsafe_unretained
[s length];
[s length];
// no release -- we used __unsafe_unretained
}
@end
Cuando se compiló con Xcode 4.3.2 en modo de lanzamiento, el ensamblaje (de modo que puedo entenderlo) contenía llamadas a objc_retain
y objc_release
al inicio y al final del segundo método. ¿Que esta pasando?
Este no es un gran problema, pero este tráfico de retención / liberación adicional se muestra cuando se utilizan los instrumentos para perfilar el código sensible al rendimiento. Parece que puede decorar los argumentos de los métodos con __unsafe_unretained
para evitar esta retención / liberación adicional, como lo he hecho en el tercer ejemplo, pero al hacerlo se siente bastante desagradable.
No se incrementará detrás de las escenas. Bajo ARC, si el objeto es Fuerte, simplemente permanecerá vivo hasta que no haya más punteros a él. Pero esto realmente no tiene nada que ver con que el objeto se pase como parámetro o no.
Pasar como parámetro no aumenta, en general, la cuenta de retención. Sin embargo, si se lo pasa a algo como NSThread
, se documenta específicamente que retendrá el parámetro para el nuevo hilo.
Entonces, sin un ejemplo de cómo pretende iniciar este nuevo hilo, no puedo dar una respuesta definitiva. En general, sin embargo, deberías estar bien.
Vea esta respuesta en la lista de correo Objc-language:
Cuando el compilador no sabe nada sobre el comportamiento de administración de memoria de una función o método (y esto sucede mucho), el compilador debe asumir:
1) Que la función o el método podría reorganizar o reemplazar completamente el gráfico de objetos completo de la aplicación (probablemente no lo hará, pero podría hacerlo). 2) Que la persona que llama podría ser un código de recuento de referencia manual y, por lo tanto, la vida útil de los parámetros pasados no es realmente conocida.
Dado # 1 y # 2; y dado que ARC nunca debe permitir que un objeto se desasigne prematuramente, estas dos suposiciones obligan al compilador a retener los objetos pasados con mayor frecuencia.
Creo que el principal problema es que el cuerpo de su método podría llevar a que se liberen los argumentos, por lo que ARC tiene que actuar de manera defensiva y retenerlos:
- (void) processItems
{
[self setItems:[NSArray arrayWithObject:[NSNumber numberWithInt:0]]];
[self doSomethingSillyWith:[items lastObject]];
}
- (void) doSomethingSillyWith: (id) foo
{
[self setItems:nil];
NSLog(@"%@", foo); // if ARC did not retain foo, you could be in trouble
}
Esa también podría ser la razón por la que no ve la retención adicional cuando solo hay una llamada en su método.