NSTimer容易陷进去的坑

前面个项目有用NSTimer做个短信验证码的定时器, 遇到了这个问题.  

关于NSTimer, 有一点需要特别注意:NSTimer会持有target(Remember that NSTimer Retains Its Target)。

  • 一个NSTimer对象在触发时会保留目标直到计时器被显式的设置无效。(NSTimer invalidate)

  • 如果调用者保存了NSTimer, 所以用完之后应该立即销毁定时器(或者退出当前视图的销毁等) 否则, 会有内存泄漏。

  • 使用块方式可以使NSTimer对象实现打破这种循环引用的扩展。为了使这个块实现作为NSTimer的公共接口一部分,可以把这个块实现方法加到NSTimer的类扩展中。 
    代码

  • // Example showing retain cycle
    #import <Foundation/Foundation.h>
    
    @interface EOCClass : NSObject
    - (void)startPolling;
    - (void)stopPolling;
    @end
    
    @implementation EOCClass {
        NSTimer *_pollTimer;
    }
    
    - (id)init {
        return [super init];
    }
    
    - (void)dealloc {
        [_pollTimer invalidate];
    }
    
    - (void)stopPolling {
        [_pollTimer invalidate];
        _pollTimer = nil;
    }
    
    - (void)startPolling {
        _pollTimer = 
        [NSTimer scheduledTimerWithTimeInterval:5.0
                                         target:self
                                       selector:@selector(p_doPoll)
                                       userInfo:nil
                                        repeats:YES];
    }
    
    - (void)p_doPoll {
        // Poll the resource
    }
    
    @end
    
    
    // Block support for NSTimer
    #import <Foundation/Foundation.h>
    
    @interface NSTimer (EOCBlocksSupport)
    
    + (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                     block:(void(^)())block
                                   repeats:(BOOL)repeats;
    
    @end
    
    @implementation NSTimer (EOCBlocksSupport)
    
    + (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                     block:(void(^)())block
                                   repeats:(BOOL)repeats
    {
        return [self scheduledTimerWithTimeInterval:interval
                                             target:self
                               selector:@selector(eoc_blockInvoke:)
                                           userInfo:[block copy]
                                            repeats:repeats];
    }
    
    + (void)eoc_blockInvoke:(NSTimer*)timer {
        void (^block)() = timer.userInfo;
        if (block) {
            block();
        }
    }
    
    @end
    
    
    // Changing to use the block - still a cycle
    - (void)startPolling {
        _pollTimer = 
        [NSTimer eoc_scheduledTimerWithTimeInterval:5.0
                                              block:^{
                                                  [self p_doPoll];
                                                     }
                                            repeats:YES];
    }
    
    
    // No cycle using weak references
    - (void)startPolling {
        __weak EOCClass *weakSelf = self;
        _pollTimer = 
        [NSTimer eoc_scheduledTimerWithTimeInterval:5.0
                                              block:^{
                                   EOCClass *strongSelf = weakSelf;
                                   [strongSelf p_doPoll];
                                                     }
                                            repeats:YES];
    



所以,如果你不想实现block的方式,在dealloc中记得invalidate timer就可以了。 也可以看看这篇文章Defeating NSTimer Retain Cycle

Defeating NSTimer Retain Cycle

If you’ve done any coding with NSTimer and ARC you’ll know the pain of having to manually invalidate your timer before its owner can be deallocated.

Typically, you’ll have to have your object’s parent reach into the object’s inner workings and invalidate the timer at the appropriate time. This is inconvenient and breaks encapsulation.

I have a UITableViewCell which utilizes a NSTimer to keep track of energy regeneration, and I was determined to make it work without any outside intervention.

My solution was to utilize the UITableViewCell’s willMoveToWindow: method like so:

- (void)willMoveToWindow:(UIWindow *)newWindow
{
    [super willMoveToWindow:newWindow];
    if (newWindow == (id)[NSNull null] || newWindow == nil) {
        [self stopTimer];
    }
    else {
        [self refreshEnergy];
    }
}

- (void)stopTimer
{
    [timer invalidate];
    timer = nil;
}

- (void)refreshEnergy
{
    if (timer == nil) {
        timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(refreshEnergy) userInfo:nil repeats:YES];
    }
    ...
}

Now the UITableVIewCell automatically starts and stops the repeating timer as needed and deallocates correctly.

A note: You might try to simple use a weak reference to self in the NSTimer constructor but you’ll find that the timer must be invalidated to deallocate correctly.This WON’T work:

__weak MyClass *weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:weakSelf selector:@selector(refreshEnergy) userInfo:nil repeats:YES];

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值