2012-04-25 14 views
18

Al compilar con ARC, los argumentos del método a menudo parecen conservarse al principio del método y liberarse al final. Este par de retención/liberación parece superfluo, y contradice la idea de que ARC "produce el código que usted habría escrito de todos modos". Nadie en esos días oscuros anteriores al ARC realizó una retención/liberación extra en todos los argumentos del método solo para estar seguros, ¿o sí?¿Por qué ARC retiene los argumentos del método?

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 compila con Xcode 4.3.2 en modo de lanzamiento, el conjunto (de tal manera que soy capaz de entenderlo) contenía llamadas a objc_retain y objc_release al comienzo y al final de la segundo método. ¿Que esta pasando?

Esto no es un gran problema, pero este tráfico retenido/liberado adicional aparece cuando se utilizan instrumentos para perfilar código sensible al rendimiento. Parece que puedes decorar los argumentos del método con __unsafe_unretained para evitar esta retención/liberación extra, como he hecho en el tercer ejemplo, pero hacerlo te parece bastante desagradable.

+3

¿Alguna idea de por qué no ocurre en el primer ejemplo? – Thilo

+0

Siempre puede verificar el código generado usando la vista del asistente de desmontaje. – Danra

Respuesta

17

Ver this reply de la lista de correo ObjC-idioma:

Cuando el compilador no sabe nada sobre el comportamiento de la gestión memoria de una función o un método (y esto sucede mucho ), entonces el compilador debe asumir:

1) que la función o un método pueden reorganizar o reemplazar por completo todo el gráfico de objetos de la aplicación (que probablemente no lo hará, pero podría). 2) Que la persona que llama puede ser un código de referencia manual contado, y por lo tanto, la vida útil de los parámetros pasados ​​no es realista conocible.

Dado # 1 y # 2; y dado que ARC debe nunca permitir que un objeto sea desasignado prematuramente, estas dos suposiciones fuerzan al compilador a retener objetos pasados ​​en la mayoría de los casos.

creo que el problema principal es que el cuerpo de su método podría dar lugar a los argumentos de ser liberado, para que ARC tiene que actuar a la 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 
} 

que también podría ser la razón por la que no ves el retener extra cuando solo hay una sola llamada en tu método.

+5

En pocas palabras, en el segundo ejemplo, la primera llamada a '[s length]' podría causar 's' ser desasignado a menos que se conserve primero, por lo que la segunda llamada podría estar en un objeto no existente. – JeremyP

+0

"el compilador no sabe nada sobre el comportamiento de gestión de memoria de una función o método". Uno pensaría que podría descubrir (o que Apple le dijera) que NSString: length se porta bien. ¿Hay algún ejemplo cuando el compilador tiene suficiente información útil? ¿O realmente sucede esto con las llamadas al método * all *? – Thilo

+0

Supongo que es muy difícil de valer la pena. "Bien educado" probablemente se reduzca a "no libera nada aparte de las variables locales", y esa es una garantía bastante sólida. El costo de las retenciones y versiones extra en los argumentos del método es efectivamente cero para la mayoría de las personas y cuando realmente te duele, '__unsafe_unretained' soluciona el caso si sabes lo que estás haciendo. – zoul

1

No aumentará entre bastidores. En ARC, si el objeto es fuerte, simplemente permanecerá vivo hasta que no haya más indicadores fuertes para él. Pero esto realmente no tiene nada que ver con el objeto que se pasa como parámetro o no.

2

Pasar como parámetro no aumenta, en general, el conteo de retención. Sin embargo, si lo pasa a algo como NSThread, está específicamente documentado que conservará el parámetro para el nuevo subproceso.

Así que sin un ejemplo de cómo está pensando comenzar este nuevo hilo, no puedo darle una respuesta definitiva. En general, sin embargo, deberías estar bien.

2

Incluso la respuesta del alma es correcto, es un poco más de lo que debería ser:

Se retiene, porque la referencia pasado se asigna a una variable fuerte, la variable de parámetro. Esto y solo esta es la razón del par de retención/liberación. (Establezca el parámetro var en __weak y ¿qué ocurre?)

¿Se podría optimizar? Sería como optimizar cada par de retención/liberación en variables locales, porque los parámetros son variables locales. Esto se puede hacer si el compilador entiende el código de agujero dentro del método, incluidos todos los mensajes enviados y las llamadas a funciones. Esto se puede aplicar que rara vez ese clang incluso no intenta hacerlo. (Imagine que el arg señala a una persona (solo) que pertenece a un grupo y el grupo es desasignado: la persona también sería destrabada.)

Y sí, no retener args en MRC era una es algo peligroso, pero los desarrolladores saben que su código es bueno, que optimizan la retención/liberación sin pensarlo.

Cuestiones relacionadas