2009-12-09 10 views
5

siguiente es mi código:Problema invalidar NSTimer en dealloc

archivo .h:

#import "Foundation/Foundation.h" 
@interface GObject:NSObject{ 
    NSTimer* m_Timer; 
} 
@property(nonatomic, retain) NSTimer* m_Timer; 

- (void)Initialize; 
- (void)TimerCallback:(NSTimer*)pTimer; 
@end 

archivo .m:

@implementation GObject 

@synthesize m_Timer 

- (void) Initialize{ 
    self.m_Timer = [NSTimer scheduledTimerWithTimeInterval:5.0 
         target:self 
         selector: @selector(TimerCallback:) 
         userInfo: nil 
         repeats: YES]; 

} 

- (void)TimerCallback:(NSTimer*)pTimer { 
    //Some Code 
} 
- (void)dealloc { 
    [m_Timer invalidate]; //--Crashes Here 
    [m_Timer release]; 
    m_Timer = nil; 
    [super dealloc]; 
} 
@end 

Ahora cuando el dealloc es llamado, el programa se bloquea en la línea que invalida el temporizador. Las siguientes dos líneas ni siquiera se llaman. Aparece un error de "EXC_BAD_ACCESS". ¿Alguien puede decirme por qué podría estar pasando eso, y cuál es la forma correcta de detener y liberar una variable de miembro de NSTimer en una clase?

+0

¿Está creando el getter/setter de la propiedad con @synthesize? – gerry3

+0

Sí, necesito acceder a este temporizador desde fuera de mi clase también. Aunque setter no es requerido. – Prashant

Respuesta

16

Hice algunas investigaciones y pruebas, y pude encontrar la respuesta a mi propia pregunta. Ok, aquí va:

Cuando asignamos self como destino al NSTimer, el temporizador guarda una referencia a nuestro objeto. Si el temporizador se repite (o tiene un período de tiempo largo), no se invalidaría por sí mismo (o tomaría demasiado tiempo para invalidar automáticamente, si no se repite). Entonces, incluso si el objeto se lanza mientras tanto, no llamaría al método dealloc, ya que el conteo de retención sería al menos de 1. Ahora, estaba intentando forzar la ubicación de mi objeto llamando a lanzamientos repetidos hasta que retenga el conteo se convirtió en 0. Ese fue mi error.

Pero si no lo hace, su objeto se mantendrá con vida, y eventualmente tendrá una pérdida de memoria ya que pierde el resto de las referencias a ese objeto por varias versiones. El único que se conservará será el NSTimer. Esto suena como una situación de punto muerto. Mi código se bloqueó, porque cuando el delloc intentó invalidar el NSTimer, intentó liberar la referencia que estaba reteniendo. Pero como había sido un listillo y había reducido el conteo de retención a 0, eso causaría una excepción de memoria.

Para resolver esto, primero limpié mi acto y eliminé el código para deslocalizar forzosamente el objeto.Entonces, justo antes de querer que el objeto fuera asignado, llamé a la función invalidada de NSTimer. Esto liberó la instancia de destino que tenía el temporizador. Después de eso, al llamar al release en mi objeto, se lo asignó con éxito.

La línea inferior es, si su objeto tiene NSTimers que se repiten o no invalidan (superan) automáticamente, nunca los invalide en la función delloc. El delloc no se ejecutará hasta que el temporizador retenga la instancia de su objeto. En su lugar, tiene una función de limpieza para invalidar los temporizadores, antes de liberar el objeto. Esa es la solución que se me ocurrió. Si hay uno mejor, ciertamente quiero saberlo.

5

Hay algunas cosas que debe resolver para limpiar esto.

Su declaración de clase está un poco desajustada. Si desea heredar de NSObject, la sintaxis correcta es:

@interface GObject : NSObject 

En su aplicación, se deben implementar - (id)init en lugar de - (void)Initialize. No hay ningún método de instancia - (void)Initialize ... hay un método estático + (void)initialize. Tenga en cuenta el + y la diferencia en mayúsculas, que es significativo): el método initialize se llama una vez en su programa antes de que la clase reciba su primer método.

En este caso, su método Initialize no se está llamando en absoluto (se escribe mal, y es un método de instancia en lugar de un método estático). En su lugar, se desea implementar init, que es el inicializador designado para casos NSObject:

- (id)init { 
    if (self = [super init]) { 
     self.m_Timer = [NSTimer scheduledTimerWithTimeInterval:5.0 
        target:self 
        selector: @selector(TimerCallback:) 
        userInfo: nil 
        repeats: YES]; 
    } 
    return self; 
} 

Por último, asegúrese de usar el símbolo @ antes de la instrucción de propiedad:

@property(nonatomic, retain) NSTimer* m_Timer; 

Y no se olvide para sintetizarlo en su implementación:

@implementation GObject 

@synthesize m_Timer; 
+0

Sí, esto es lo que está causando el bloqueo. El temporizador nunca se ha inicializado porque nunca se llama al método '-Inicializar', y por lo tanto, nunca crea el temporizador. Luego, su método dealloc que llama a lanzar en un puntero que nunca se inicializó, este puntero podría estar apuntando a cualquier objeto al azar en el programa, o incluso a la basura. – Jasarien

+0

Lo siento, debería haber mencionado que estaba escribiendo este código de memoria ya que no estoy cerca de mi Mac actualmente. En mi código real, hice todo eso, excepto la parte intrínseca. – Prashant

+0

He corregido mi código anterior según su consejo. En cuanto al método Initialize, desde que lo creé, puedo llamarlo cada vez que estoy instanciando mi objeto, como GObject * a = [GObject alloc]; [a Initialize]; Entiendo que esta no es una buena práctica, pero estoy seguro de que está permitida. Tenga en cuenta que se llama al método Initialize y se activa el temporizador. Así que, ese no es el problema. Gracias por ayudarme a corregir mi publicación. – Prashant

0

Tengo el mismo problema de pérdida de memoria al utilizar NSTimer repetido en uno de los controladores de vista. Este controlador de vista no se liberará cuando debería. Finalmente resolví este problema moviendo mi temporizador al código delegado de la aplicación principal donde no hay necesidad de liberar el temporizador.

2

Bueno, tengo que decir que puede consultar la documentación del desarrollador de apple, como la referencia de clase de Apple. Puede ver desde allí que cuando se invalida: se invoca el método, se llamará al método de liberación del temporizador antes de invalidar: el método retorna.

0

Otra solución sería crear un objeto separado que se ocupe de la devolución de llamada NSTimer (algunos TimerController). Ese objeto, si es necesario, puede llamar a los métodos en su controlador self.

Ahora el temporizador no mantener una referencia a self y cuando self consigue desasignar, - dealloc debe ser llamado y se puede invalidar y ajustar a cero el TimerController y el propio temporizador.