NSTimer佛玄,沒錯(cuò)硼一,定時(shí)器。我們開發(fā)中經(jīng)常使用到的一個(gè)東西梦抢,而且我們?cè)谑褂盟臅r(shí)候差不多都是按照以下代碼來使用的:
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)timerAction:(NSTimer *)timer
{
NSLog(@"%@", timer);
}
but般贼,如果你將timer所屬的控制器推出后,發(fā)現(xiàn)timer此時(shí)還在執(zhí)行奥吩,這是因?yàn)閠imer的執(zhí)行需要依賴于runloop哼蛆,在timer創(chuàng)建好放入runloop之后,并且如果timer是循環(huán)執(zhí)行的圈驼,如果不顯示調(diào)用invalidate方法人芽,那么timer是停不下來的。
同時(shí)绩脆,如果此時(shí)你重寫了這個(gè)控制器的dealloc方法萤厅,并且讓這個(gè)控制器pop出navigation所管理的棧時(shí)(我的這個(gè)控制器是由navigation所push出來的)橄抹,你會(huì)發(fā)現(xiàn)dealloc方法并不會(huì)執(zhí)行,這表明控制器并沒有被釋放惕味,這是為什么呢楼誓,這是因?yàn)镹STimer在添加target時(shí),會(huì)對(duì)這個(gè)target進(jìn)行retain名挥。所以就會(huì)造成上面這種情況:控制器要釋放疟羹,就要釋放它的所有實(shí)例變量,當(dāng)釋放到timer時(shí)禀倔,timer要釋放他所持有的target榄融,而此時(shí)的target是該控制器,所以造成了循環(huán)引用救湖,從而造成了內(nèi)存泄露愧杯。
要避免這種情況,我能想到的一個(gè)辦法就是在設(shè)定timer的target時(shí)鞋既,將target-action保存力九,target改設(shè)置為另一個(gè)和timer不存在引用關(guān)系的變量,進(jìn)而避免泄露邑闺。
代碼如下:
首先定義一個(gè)用來保存target-action的對(duì)象
@interface PltTimerTarget : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *timer;
@end
@implementation PltTimerTarget
- (void)pltTimerTargetAction:(NSTimer *)timer
{
if (self.target) {
//該方法會(huì)在RunLoop為DefaultMode時(shí)才會(huì)調(diào)用跌前,與timer的CommonMode沖突
//[self.target performSelector:self.selector withObject:timer afterDelay:0.0];
//該方法可以正常在CommonMode中調(diào)用,但是會(huì)報(bào)警告
//[self.target performSelector:self.selector withObject:timer];
//最終方法
IMP imp = [self.target methodForSelector:self.selector];
void (*func)(id, SEL, NSTimer*) = (void *)imp;
func(self.target, self.selector, timer);
} else {
[self.timer invalidate];
self.timer = nil;
}
}
@end
然后我們自己定義一個(gè)方法陡舅,用來設(shè)置timer(這里我定義的是默認(rèn)循環(huán)的timer抵乓,這種比不循環(huán)的要常用)
+ (instancetype)pltScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo
{
PltTimerTarget *timerTarget = [[PltTimerTarget alloc] init];
timerTarget.target = aTarget;
timerTarget.selector = aSelector;
NSTimer *timer = [NSTimer timerWithTimeInterval:ti target:timerTarget selector:@selector(pltTimerTargetAction:) userInfo:userInfo repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
timerTarget.timer = timer;
return timerTarget.timer;
}
這樣,我們就能在代碼中正常使用timer了
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer pltScheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerAction:) userInfo:@"userInfo"];
//這樣創(chuàng)建的timer蹭沛,target的dealloc方法不會(huì)執(zhí)行臂寝,因?yàn)閠imer會(huì)持有target章鲤,進(jìn)而造成循環(huán)引用
// self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerAction:) userInfo:@"userInfo" repeats:YES];
}
- (void)timerAction:(NSTimer *)timer
{
NSLog(@"%@", timer.userInfo);
}
- (void)dealloc
{
[self.timer invalidate];
self.timer = nil;
NSLog(@"%@ dealloc", self);
}
此時(shí)摊灭,dealloc方法是會(huì)執(zhí)行的,并且能順利的將timer從runloop中停止败徊,避免了內(nèi)存泄露和資源浪費(fèi)帚呼。
Demo地址