Consider view controller with strong(or weak, the same) NSTimer
property:
__weak __typeof(self) ws = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:ws selector:@selector(timerTigger:) userInfo:nil repeats:YES];
But why does this view controller not invoke dealloc
method, whether I pass strong
or weak
reference to self
?
Here is the detailed code:
#import "SecondViewController.h"
@interface SecondViewController ()
@property (nonatomic, weak) NSTimer *timer;
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
__weak __typeof(self) ws = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:ws selector:@selector(timerTigger:) userInfo:nil repeats:YES];
}
- (void)timerTigger:(id)timer {
NSLog(@"do someting");
}
- (void)dealloc {
NSLog(@"SecondViewController dealloc");
}
NSTimer
maintains strong reference to its target
until the timer is invalidated. You don't get to choose whether NSTimer
establishes a weak or strong reference. When you pass a weak reference, as long as ws
is not nil
by the time you start the timer (which it obviously won't be, in this case), NSTimer
will establish a strong reference to whatever target
pointed. Whether scheduledTimerWithTimeInterval
establishes a strong or weak reference is not some inherent characteristic of the pointer that you passed to it, but rather a question of what that method does with that pointer it was provided.
To fix this strong reference behavior, you can adopt one of the following patterns:
use the block based rendition of NSTimer
and use "weak self" pattern inside the block;
@interface ViewController ()
@property (nonatomic, weak) NSTimer *timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
typeof(self) __weak weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:true block:^(NSTimer * _Nonnull timer) {
[weakSelf timerTigger];
}];
}
- (void)timerTigger {
NSLog(@"do something");
}
- (void)dealloc {
[self.timer invalidate];
}
@end
use GCD timer, which is also block based and therefore can also easily be configured to not keep strong reference; or
@interface ViewController ()
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
typeof(self) __weak weakSelf = self;
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.timer, ^{
[weakSelf timerTigger];
});
dispatch_resume(self.timer);
}
- (void)timerTigger {
NSLog(@"do something");
}
@end
use NSTimer
with target/selector, but invalidate
the timer somewhere logical (e.g. viewDidDisappear
or the like).
@interface ViewController ()
@property (nonatomic, weak) NSTimer *timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerTigger:) userInfo:nil repeats:true];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.timer invalidate];
}
- (void)timerTigger:(NSTimer *)timer {
NSLog(@"do something");
}
@end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With