一、首先介绍NSTimer一些其他注意点
1.scheduledTimerWith的函数创建并安排到runloop的default mode中。会出现其他mode时timer得不到调度的问题。最常见的问题就是在UITrackingRunLoopMode,即UIScrollView滑动过程中定时器失效。
解决方式就是把timer add到runloop的NSRunLoopCommonModes。
2.iOS是通过runloop作为消息循环机制,主线程默认启动了runloop,可是子线程没有默认的runloop,因此,我们在子线程启动定时器是不生效的。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSTimer* timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(Timered:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
});
二、不是所有的NSTimer都会造成循环引用。就像不是所有的block都会造成循环引用一样。以下两种timer不会有循环引用:
非repeat类型的。非repeat类型的timer不会强引用target,因此不会出现循环引用。
block类型的,新api。iOS 10之后才支持,因此对于还要支持老版本的app来说,这个API暂时无法使用。当然,block内部的循环引用也要避免。
注意点:timer被加到runloop中,不会自动释放,不是解决了循环引用,target就可以释放了,别忘了在持有timer的类dealloc的时候执行invalidate。
找到的一些解决办法
- (void)viewDidLoad {
[super viewDidLoad];
__weak id weakSelf = self;
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer *timer) {
NSLog(@"block %@",weakSelf);
}];
}
@implementation NSTimer(BlockTimer)
+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats blockTimer:(void (^)(NSTimer *))block{
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(timered:) userInfo:[block copy] repeats:repeats];
return timer;
}
+ (void)timered:(NSTimer*)timer {
void (^block)(NSTimer *timer) = timer.userInfo;
block(timer);
}
@end
解释:将强引用的target变成了NSTimer的类对象。类对象本身是单例的,是不会释放的,所以强引用也无所谓。执行的block通过userInfo传递给定时器的响应函数timered:。循环引用被打破的结果是:
timer的使用者强引用timer。
timer强引用NSTimer的类对象。
timer的使用者在block中通过weak的形式使用,因此是被timer弱引用。
网友评论