标签:
计时器可以指定绝对的日期和时间,以便到时执行任务也可以指定执行的任务的相对延迟时间,还可以重复运行任务。计时器要和runloop相关联,运行循环到时候会触发任务。虾米昂这个方法可以创建并预先安排到当前运行循环中:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
target与selector参数表示计时器将在哪个对象上调用哪个方法。可以指定时间执行任务,也可以令其反复执行任务,直到自己手动将其关闭。计时器会保留其目标对象,等其失效时再释放此对象。若是重复模式需自己调用invalidate才能令其停止。
由于计时器会保留其目标对象,所以反复执行任务会导致“保留环”(即循环引用)。问题代码如下:
#import "testViewController.h"
@interface testViewController (){
NSTimer *_time;
}
- (void)startPolling;
- (void)stopPolling;
@end
@implementation testViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200, 40)];
btn.backgroundColor = [UIColor redColor];
[btn addTarget:self action:@selector(clickBtn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
[self startPolling];
}
-(void)dealloc{
NSLog(@"testVC dealloc");
[_time invalidate];
}
-(void)startPolling{
_time = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(doPoll) userInfo:nil repeats:YES];
}
-(void)stopPolling{
[_time invalidate];
_time = nil;
}
- (void)doPoll{
NSLog(@"do something");
}
- (void)clickBtn{
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
计时器的目标对象是self,所以保留此实例。但计时器是实例变量存放的,所以实例也保留了计时器。于是就产生了“保留环”。若想在系统回收奔雷实例的时候令计时器无效,从而打破保留环,那会陷入死结,因为_time对象有效时,该实例对象保留计数不会降为0,因为也不会调用dealloc方法,从而也无法调用invaildate方法,所以计时器一直处于有效果状态。从而导致内存泄露。若想释放必须主动调用stopPolling方法,坦诺作为公开API给别人使用,无法保证他人一定会调用此方法。
解决方式:用block可解决,可为计时器添加以下功能:
#import <Foundation/Foundation.h>
@interface NSTimer (JBlocksSupport)
+ (NSTimer *)j_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats;
@end
@implementation NSTimer (JBlocksSupport)
+ (NSTimer *)j_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(j_blockInvoke:) userInfo:[block copy] repeats:repeats];
}
+ (void)j_blockInvoke:(NSTimer *)timer{
void (^block) () = timer.userInfo;
if (block) {
block();
}
}
@end
解释:将计时器执行的任务封装成block,调用该方法时将block作为useInfo参数传进去。只要计时器有效,就会一直保留block,传入时需拷贝到“堆”上,否则可能会失效。target是类对象,此处也有保留环,但类对象无需回收,因此无需担心。使用方法如下:
-(void)startPolling{
__weak testViewController *weakSelf = self;
_time = [NSTimer j_scheduledTimerWithTimeInterval:1 block:^{
[weakSelf doPoll];
} repeats:YES];
}
先定义弱引用,令其指向self,块捕获该弱引用,即self不会被计时器所保留。这样,在外界指向该类的实例的最后一个引用将其释放,则该实例便可被系统回收,会调用dealloc方法,还会调用invalidate方法,使计时器失效。
记住:若将计时器设置为重复,则需调用invalidate将其失效,若一次性的话,在其触发完任务之后也会失效。
标签:
原文地址:http://www.cnblogs.com/xiaoxiaobin/p/4590600.html