iOS开发中,针对循环引用的问题,会有很多方面,block,代理,自循环,多循环,还有一个就是Timer的循环引用
在定时器使用方面,并不是简单的把定时器的属性置为weak就行了,因为假设定时器是在主线程创建的,那么会有一个RunLoop强引用着这个定时器,而定时器又被Controller强引用这,并不能达到破除循环引用的问题
这里我的想法是使用一个中间变量,而这个中间变量就是weak的属性
我们先定一个一个定时器的分类,.h文件是这样的
@interface NSTimer (weakTimer)
+ (NSTimer *)scheduledWeakTimerWithTimerInterval:(NSTimeInterval)interval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats;
@end
我们先不看.m的实现,先看看这个中间变量是如何创建的
@interface TimerWeakObject : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *timer;
- (void)fire:(NSTimer *)timer;
@end
在这个中间变量的头文件里,我们定义了三个属性,target,SEL,timer,第一个跟最后一个都是weak的属性,为了后面的破除循环引用使用的。
再看看这个中间变量的.m实现
@implementation TimerWeakObject
- (void)fire:(NSTimer *)timer {
if (self.target
) {
if ([self.target respondsToSelector:self.selector]) {
[self.target performSelector:self.selector withObject:timer.userInfo];
}
}else {
[self.timer invalidate];
}
}
@end
这里主要是为了创建回调函数,但是做了判断是否有这个target,这个target就很关键了,如果这个taget不存在了,就会走定时器的关闭方法,而这个target也是由外面传进来的,也就是控制器那个self,那么在控制器这层出栈的时候,因为使用中间变量是弱引用的,并不会造成循环引用,那么这时候target会被清除,那么既然target不存在了,自然就走到else的操作里了。
接下来我们看看这个分类的实现方法
@implementation NSTimer (weakTimer)
+ (NSTimer *)scheduledWeakTimerWithTimerInterval:(NSTimeInterval)interval target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats {
TimerWeakObject *object = [[TimerWeakObject alloc]init];
object.target = aTarget;
object.selector = aSelector;
object.timer = [NSTimer scheduledTimerWithTimeInterval:interval target:object selector:@selector(fire:) userInfo:userInfo repeats:YES];
return object.timer;
}
@end
在这个中间对象的初始化中,我们传入了target和SEL,又针对中间变量的time进行了初始化,并把自身赋值给了object.timer,最后返回这个timer。
我们来看看控制器里是如何使用这个分类的,其实跟一般的定时器使用方法是一样的
@interface ViewController1 ()
@property (nonatomic,strong) NSTimer *timer;
@end
@implementation ViewController1
- (void)dealloc {
NSLog(@"111");
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.timer = [NSTimer scheduledWeakTimerWithTimerInterval:4 target:self selector:@selector(test) userInfo:nil repeats:YES];
}
- (void)test {
NSLog(@"aaaaaa");
}
@end
最后打印可以看到dealloc是执行了打印的,到这就完成了一个定时器的使用了
网友评论