• object-c编程tips-timer


    object-c定时器

    object-c定时器会自己主动retain当前的使用者,假设不注意调用invalidate,则非常easy引起循环引用导致内存泄露。以下的思路提供了一套还算可行的解决方式。


    举例:

    常常在viewController中有可能有自己主动刷新界面的需求。 获取数据失败后。每隔10秒自己主动刷新又一次获取数据,这个时候使用NSTimer是一个非常方便的事情。普通情况下直接创建一个NSTimer的repeat对象,然后实现相应的timerFireMethod方法。 当用户主动点击返回button时候,此界面应该被释放。可是因为NSTimer retain了当前的viewController,导致界面内存泄露。 你可能会说在dealloc中调用invalidate,可是必须明确dealloc根本就不会调用,当然viewDidDisappear也一样不会被调用。


    前一段时间看了effective object-c,学习了一种非常好的思想,现分享出来。

    给NSTimer加入一个类别,使用block的方式传递timerFireMethod。代码例如以下:

    @implementation NSTimer(LPBLocks)
    
    +(NSTimer*) lpScheduleTimerWithTimerInternal:(NSTimeInterval)interval
                                           block:(void(^)())block
                                         repeats:(BOOL)repeats
    {
        return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(lpTimerBlockInvoke:) userInfo:[block copy] repeats:repeats];
    }
    +(void)lpTimerBlockInvoke:(NSTimer*)timer
    {
        void(^block)() = timer.userInfo;
    
        if(block){
            block();
        }
    }
    @end

    这个scheduledTimer方法也会retain target,可是因为这是一个类方法。它保留的是类对象,因此也就不会有什么问题。

    它传入要运行的block, 然后在回调函数中通过userInfo得到block,并运行。


    改进:

    这个已经是一个非常大的改进了。我们能够在代码中放心的传入block代码。只是细致思考一下。假设在block中引入了viewController的成员,并且timer又作为成员变量存在于viewController中。

    比如例如以下的代码:

    @interface LPNextViewController ()
    {
        NSTimer*  refreshTimer;
    }

    这样viewController和refreshTimer又陷入了循环引用的逻辑圈里。当然能够在block中使用weak_self的方式避免循环引用,可是写起代码来总是有些不顺手。并且还必需要外部使用者显式的进行。

    于是非常easy想到。应该封装到一个专门的LPTimer类中。它负责持有NSTimer。同一时候NSTimer的block使用LPTimer的weak版本号。

    @interface LPTimer ()
    {
        NSTimer* _pollTimer;
    
        //timer selector
        __weak id _weak_target;
        SEL _selector;
        id  _userInfo;
    }
    @end

    -(void)scheduleTimerWithTimerInternal:(NSTimeInterval)interval
                                     target:(id)target
                                   selector:(SEL)aSelector
                                   userInfo:(id)userInfo
                                    repeats:(BOOL)repeats
    {
        __weak id weak_self = self;
    
        _weak_target = target;
        _selector = aSelector;
        _userInfo = userInfo;
    
        //借用第一个版本号的block思想
        //使用了第二层间接,调用_weak_target的aSelector方法。
        //这样能够把stopTimer给封装进去。外部不须要管理timer的stop。
        _pollTimer = [NSTimer lpScheduleTimerWithTimerInternal:1 block:^{
            [weak_self doTimer];
        } repeats:repeats];
    }

    上面的代码LPTimer持有NSTimer对象。而NSTimer运行的block使用的是weak_self。

    它在timer触发的时候调用自身的doTimer方法。在doTimer中负责将方法传递给外部的使用者。

    -(void)doTimer
    {
        if ([_weak_target respondsToSelector:_selector]) {
            [_weak_target performSelector:_selector withObject:self];
        }
        else{
            DLog(@"WARNNING: unknown selector");
        }
    }


    _weak_target是外部的使用者。 外部的使用者能够将LPTimer看成是一个普通的对象即可,持有它也不会有什么问题。 LPTimer保留一个弱引用指向外部的使用者。在时间到timer触发的时候,会先调到NStimer的block中。然后传递到LPTimer的doTimer中。然后调用到_weak_target的selector中。


    必须注意释放NStimer对象,在LPTimer释放的时候调用NSTimer的invalidate方法。

    -(void)stopTimer
    {
        DLog(@"");
        [_pollTimer invalidate];
    }
    -(void)dealloc
    {
        [self stopTimer];
        DLog(@"");
    }


    事实上。使用者都是使用的LPTimer类,那么应该让LPTimer表现的和NSTimer的行为一模一样, 使用组合方式的适配器模式就能够轻松搞定。


    总结:

    主要的思想就是NSTimer会retain一个对象,如今让它retain类对象。

    当时候到来进行触发的时候,由NSTimer类对象触发到Block中。继而触发到外部的LPTimer普通对象中。

    在普通对象中我们就能够自由的进行处理了。使用weak_target使LPTimer弱引用外部使用者,断开外部使用者与LPTimer的关联。

    使用weak_self断开LPTimer与NStimer的循环关联。 个人觉得还算不错的思想, 有须要的欢迎讨论。





  • 相关阅读:
    TCP的状态 (SYN, FIN, ACK, PSH, RST, URG)
    理论基础+实战控制台程序实现AutoFac注入
    C# class 浅拷贝 与 深拷贝
    给定一个矩阵 A, 返回 A 的转置矩阵。
    [弹出消息] C#ShowMessageBox帮助类
    [弹出消息] C#MessageBox帮助类 (转载)
    [XML] C#XMLProcess操作Xml文档的帮助类 (转载)
    [XML] C# XmlHelper操作Xml文档的帮助类 (转载)
    [XML] resources的Xml配置文件 (转载)
    [XML] Resource帮助类
  • 原文地址:https://www.cnblogs.com/yxwkf/p/5116280.html
Copyright © 2020-2023  润新知