美文网首页
NSTimer的循环引用问题解决方案

NSTimer的循环引用问题解决方案

作者: aven_kang | 来源:发表于2021-03-02 11:56 被阅读0次

    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是执行了打印的,到这就完成了一个定时器的使用了

    相关文章

      网友评论

          本文标题:NSTimer的循环引用问题解决方案

          本文链接:https://www.haomeiwen.com/subject/qzmtqltx.html