NSTimer与循环引用

代码:

#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
原文地址:https://www.cnblogs.com/xwoder/p/4780121.html