代码:
#import "ViewController.h" @interface ViewController () @property (strong, nonatomic) NSTimer *timer; - (void)doSomething:(NSTimer *)timer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 编程提示 // A timer maintains a strong reference to its target. // This means that as long as a timer remains valid, its target will not be deallocated. // As a corollary, this means that it does not make sense for a timer’s target to try to invalidate the timer in its dealloc method, // the dealloc method will not be invoked as long as the timer is valid. // target参数 // The object to which to send the message specified by aSelector when the timer fires. // The timer maintains a strong reference to this object until it (the timer) is invalidated. NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(doSomething:) userInfo:nil repeats:YES]; // The receiver retains aTimer. // To remove a timer from all run loop modes on which it is installed, send an invalidate message to the timer. [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; self.timer = timer; } - (void)doSomething:(NSTimer *)timer { NSLog(@"%s", __func__); } - (void)dealloc { NSLog(@"%s", __func__); [self.timer invalidate]; } @end
上述代码中,由于timer会在其有效期内保持对其target的强引用,而target自身也就是当前的viewController又通过一个使用strong修饰符修饰的属性对timer产生强引用,最终产生循环引用。
并且在本例中,由于runLoop会引用被添加至其中的timer,从而产生了一个永远无法被自行打破的循环引用。
由于循环引用的存在,即便当前viewController从导航控制器的导航栈中弹出,其dealloc方法也不会被调用。所以在dealloc方法中,调用timer的invalidate方法是没有意义的。
一种可行的办法是重写viewController的viewWillDisappear:或viewDidDisappear:方法,在其方法体内调用timer的invalidate方法,从而解除timer对target即viewController的强引用,最终打破循环引用。
代码-修改版:
#import "ViewController.h" @interface ViewController () @property (strong, nonatomic) NSTimer *timer; - (void)doSomething:(NSTimer *)timer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(doSomething:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; self.timer = timer; } - (void)doSomething:(NSTimer *)timer { NSLog(@"%s", __func__); } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; NSLog(@"%s", __func__); [self.timer invalidate]; } - (void)dealloc { NSLog(@"%s", __func__); } @end