iphone - ¿Hay alguna manera de "encontrar misterios retenidos"...?
memory-management xcode4 (6)
Recientemente estaba reparando el código de alguien. Hubo una gran clase que no se trataría. Tendría que pegarle con 5 o 6 lanzamientos para obtenerlo.
Revisé cuidadosamente la gran clase y finalmente encontré las diversas cosas que debían ser lanzadas.
Esto me hizo pensar: simplemente tiene que haber una manera realmente fácil de "encontrar" todas las partes retenidas en un objeto ... ¿estoy en lo cierto?
Entonces, ¿hay una manera simple de "encontrar todas las retenciones" en un objeto? ¿Hay algún botón en XCode o Instrumentos que todos los demás conozcan?
¿Qué haces cuando no puedes encontrar un misterio retener así?
Entonces, en el universo iOS, si alguien conoce el botón "Mostrar de dónde provienen todos los objetos retenidos en este objeto" , ¡gracias!
PD Tenga en cuenta que no hay fugas, y esta pregunta no tiene ninguna relación con fugas. El objeto simplemente "perfectamente correcto" no se liberaría.
Luego ..
Solución realmente asombrosa de Fabio:
Fabio ha brindado una solución asombrosa a este problema. En nueve palabras, aquí está:
-(id)retain
{
NSLog(@"%@", [NSThread callStackSymbols]);
return ([super retain]);
}
Eso es increíblemente útil en muchas situaciones y conduce a muchas otras cosas útiles. Probablemente me has ahorrado dos semanas-hombre de trabajo por año para siempre, Fabio. ¡Gracias!
Por cierto, si solo estás entendiendo esto y luchando con la producción, vi que normalmente habrá muchos fragmentos con "UINib instantiateWithOwner:". Parece que esos serán los primeros, los fragmentos significativos seguirán.
Coloque un punto de interrupción en la clase personalizada ''retener
Puede establecer un punto de corte simbólico en retener y luego establecerlo en el método retener de la clase personalizada. El problema aquí es que retener es un método en NSObject
por lo que tendrá la opción de elegir todas las clases objetivo-c cuando coloque el punto de corte.
En este caso, sería mejor sobrescribir el método de retención de la clase personalizada con una llamada a super, por lo que no haría nada, pero podría colocar un punto de interrupción en él.
Use una acción de punto de interrupción para registrar a la persona que llama
Para agregar una acción de punto de interrupción, haga doble clic en el marcador azul. Encuentre el punto de interrupción en la lista y presione el botón + a la derecha. A continuación, elija el Debugger command
y agregue el frame 1
comando frame 1
GDB en este campo, que le mostrará la persona que llama de retener. Por esto, registras todas las conservaciones y de dónde vienen. Al registrar los lanzamientos de forma similar, puede comprobar cuál fue la versión extra.
Todavía es un poco tedioso, pero esto es lo mejor que se me ocurre.
¿Has intentado usar "Build & Analyze" en Xcode?
Es genial para que no se suelte el fondo de los objetos.
Desgraciadamente, no es posible determinar de forma programática qué es "propietario" de un objeto, ya que la idea de "propiedad del objeto" es una convención de codificación (a menos que se habilite la recolección de basura).
El registro de pila suele ser útil (suelo usar algunos puntos de interrupción con bt;continue
) pero eso solo indica la función que llamó retener, no la "imagen más grande" (por ejemplo, podría "transferir propiedad" con [ivar2 release]; ivar2 = ivar1; ivar1 = nil;
). A veces es una fuga de UIKit por lo que no tienes el código fuente y realmente tienes que ir a cavar.
Sin embargo, si no se trata de una fuga, ¡llame -release
unas cuantas veces para ver dónde se cuelga!
Instrumentos y sus cosas de administración de memoria son tus amigos. Leaks y Zombies son dos de las herramientas más valiosas disponibles. Usalos, usalos a ellos.
Producto -> Perfil (o Cmd-I)
Los instrumentos pueden mostrarle la pila de llamadas para cada malloc, liberación y retención para cualquier objeto Obj-C en su aplicación sin necesidad de cambios de código . Funciona cuando estás usando ARC, que no es el caso para la solución de Fabio.
Es realmente útil para encontrar esas reticencias misteriosas, por ejemplo, cuando un objeto simplemente no se destraba cuando debería.
Así es cómo:
- CMD + I (Producto / Perfil)
- Cuando los instrumentos aparecen, elige ''Asignaciones'' (NO fugas)
- Tu aplicación debería ejecutarse
- Haga lo que sea que su misterio retiene para suceder.
- Seleccione el instrumento ''Asignación'' en el panel izquierdo.
- Presiona CMD + 1 o selecciona el círculo con la onda a la derecha. En el panel de la esquina inferior derecha, marque la opción ''Registrar recuentos de referencia''. Esto es importante, o solo se grabarán mallocs y libres.
- En el cuadro de búsqueda en la parte superior derecha de la lista, escriba el nombre de su clase (por ejemplo, BCMyObject).
- Esto filtra la lista de ''Estadísticas'' para mostrar cuántas instancias de su clase están actualmente en vivo. La columna #Persistent muestra cuántas instancias están activas.
- Haga clic en la fila y luego en la pequeña flecha -> junto al nombre de la clase. Verá que las migas de pan muestran ''Estadísticas> Resumen de asignación> BCMyobject''
- Esto le muestra todas las instancias de dicha clase (y cuáles son en vivo).
- Seleccione una instancia y haga clic en la flecha nuevamente (esta vez por dirección)
- Ahora verá ''Estadísticas> Resumen de asignación> BCMyObject> Historial: 0xADDRESS'' en las migas de pan.
- Esto aparecerá en la lista cada vez que se retiene o libera el objeto malloc.
- Ahora, en el panel izquierdo donde estaba la opción ''Registrar referencia de recuento'', presione el icono que se parece a una barra con cuadros conectados a él o presione CMD + 3 .
- Seleccione una de las filas y verá la pila de llamadas completa que dio lugar a la llamada.
¡Fácil! (Ish)
Solo adivinando ... pero puedes sobreescribir el método de retención de la clase personalizada que llama a la súper y arrojar un buen NSLog para imprimir la pila de llamadas.
Actualiza con el código real de Joe
-(id) retain {
NSLog(@"%@", [NSThread callStackSymbols]);
return ([super retain]);
}
Otro detalle importante es que [NSThread callStackSymbols] devuelve un NSArray de NSStrings que se puede filtrar y utilizar para otros fines. Por ejemplo, en código complejo y dinámico, para verificar si un método causa que otro se dispare.
NOTA: En un entorno de ARC, primero deberá agregar el -fno-objc-arc
a los indicadores del compilador para permitirle anular la retención y la llamada a super.