正常来讲,我们开启一个Timer有几种办法,
1. 直接使用
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
这个不用关心runloop的事情,也不用关心mode,默认添加到当前runloop的默认mode
2. 首先创建Timer 然后添加到runloop里面。
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti
target:(id)target
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats
这里的添加的runloop里面还要选择mode,所以注意可能会阻塞runloop里面的相同mode下的其他进程(比如滚动tableview,timer暂停)。这是个坑。需要注意。
内存泄露
内存泄露的原因是Timer不停止,则会一直保有一份target(比如self),
. This means that as long as a timer remains valid, its target will not be deallocated
这样self如果是controller,controller 被 pop 的时候就会发生内存泄露,因而不会被销毁。解决方法首先是在
- (void)viewWillDisAppear
停止timer,并且置为nil,不过这样再pop回来timer就没了,就要重新启动timer,如果也有倒计时的话,计数就不对了。比较经济实惠的方法是用block解决。大概想法就是让Nstimer 类做为target,解决内存无法释放的问题。
@interface NSTimer (XXBlocksSupport)
+ (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats;
@end
@implementation NSTimer (XXBlocksSupport)
+ (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(xx_blockInvoke:)
userInfo:[block copy]
repeats:repeats];
}
+ (void)xx_blockInvoke:(NSTimer *)timer {
void (^block)() = timer.userinfo;
if(block) {
block();
}
}
@end
注意:以上NSTimer的target是NSTimer类对象,类对象本身是个单利,此处虽然也是循环引用,但是由于类对象不需要回收,所以没有问题。但是这种方式要注意block的间接循环引用,当然了,解决block的间接循环引用很简单,定义一个weak变量,在block中使用weak变量即可。