美文网首页
NSTimer之前我真不懂

NSTimer之前我真不懂

作者: 喝酸奶舔下盖 | 来源:发表于2018-09-17 17:48 被阅读0次

NSTimer不就是定时器吗,这个平时经常用的,

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            self->startTimer = [NSTimer scheduledTimerWithTimeInterval:self.time target:self selector:@selector(start:) userInfo:nil repeats:YES];
            [[NSRunLoop currentRunLoop] run];
        });

这样写之后不做其他处理的同学断点下dealloc方法,你会情不自禁的说三句“我擦、我擦、我擦”。(😄)

NSTimer导致的内存泄漏

少年郎之前一直都是这样用NSTimer,在排查一个页面内存泄漏的时候发现VC pop之后dealloc死活不走,该弱化的对象已经弱化,该处理的方法已经处理,即便是所有成员变量的引用计数也都一一排查确定正常之后dealloc方法还是不走,最后只能向一直认为没有问题的定时器下手,问题居然还真他妈的出现在这。向之前写的项目默哀半分钟,之前用错你了😢。
查过资料了解到,为了保证参数的生命周期,NSTimer会对target对象retain一次,做强引用。以保证即便target销毁了,定时器还能正常调用timeEvent。因为定时器要加到RunLoop中,所以RunLoop强引用着NSTimer,一般情况下你的target就是当前的控制器,如果你想让控制器如你所愿的销毁了,首先得销毁NSTimer。不然NSTimer强引用着self,self就无法销毁,从而导致内存泄漏。

销毁定时器的方法

[startTimer invalidate];
 startTimer = nil;

timer被schedule的时候,timer会持有target对象,NSRunLoop对象会持有timer。当invalidate被调用时,NSRunLoop对象会释放对timer的持有,timer会释放对target的持有。除此之外,没有途径可以释放timer对target的持有。所以解决内存泄露就必须撤销timer,若不撤销,target对象将永远无法释放。

销毁timer

如果可以的话在viewWillDisappear方法里销毁timer,或者点击返回按钮的时候销毁timer,不过这些都是局限性。如果要再push一个VC,视图消失的时候销毁timer就会有问题,返回按钮的方法里销毁还要考虑左滑手势...,这些感觉都不太“科学”。
看到有大佬把target特殊处理成不是当前控制器,timer就不再强引用self,dealloc方法自然就能正常调用。

- (void)dealloc
{
    [startTimer  invalidate];
    startTimer = nil;
}

具体实现代码

#import <Foundation/Foundation.h>

typedef void (^WXWTimerBlock)(id userInfo);

@interface WXWTimer : NSObject

+ (NSTimer *) scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      target:(id)aTarget
                                    selector:(SEL)aSelector
                                    userInfo:(id)userInfo
                                     repeats:(BOOL)repeats;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      block:(WXWTimerBlock)block
                                   userInfo:(id)userInfo
                                    repeats:(BOOL)repeats;


@end

@interface WXWTimerTarget : NSObject

@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer* timer;

@end

@implementation WXWTimerTarget

- (void)timeAction:(NSTimer *)timer {
    if(self.target) {
        
        [self.target performSelector:self.selector withObject:timer.userInfo afterDelay:0.0f];
        
    } else {
        [self.timer invalidate];
    }
}

@end


@implementation WXWTimer

+ (NSTimer *) scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      target:(id)aTarget
                                    selector:(SEL)aSelector
                                    userInfo:(id)userInfo
                                     repeats:(BOOL)repeats {
    WXWTimerTarget* timerTarget = [[WXWTimerTarget alloc] init];
    timerTarget.target = aTarget;
    timerTarget.selector = aSelector;
    timerTarget.timer = [NSTimer scheduledTimerWithTimeInterval:interval
                                                         target:timerTarget
                                                       selector:@selector(timeAction:)
                                                       userInfo:userInfo
                                                        repeats:repeats];
    return timerTarget.timer;
}

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                      block:(WXWTimerBlock)block
                                   userInfo:(id)userInfo
                                    repeats:(BOOL)repeats {
    NSMutableArray *userInfoArray = [NSMutableArray arrayWithObject:[block copy]];
    if (userInfo != nil) {
        [userInfoArray addObject:userInfo];
    }
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self
                                       selector:@selector(timerBlock:)
                                       userInfo:[userInfoArray copy]
                                        repeats:repeats];
}

+ (void)timerBlock:(NSArray*)userInfo {
    WXWTimerBlock block = userInfo[0];
    id info = nil;
    if (userInfo.count == 2) {
        info = userInfo[1];
    }
    
    if (block) {
        block(info);
    }
}


@end

NSTimer与RunLoop的关系

之前写了个类似淘宝的消息滚动控件。本来也没有注意到这个问题,换工作面试的时候面试官看到这个问我怎么实现的,直接回答说:“用NSTimer加个定时器,设置重复滚动就可以了”。面试官笑了笑没说什么。场面一度十分尴尬。回来之后查了下才知道人家想要的回答应该是下面这样的,而不是我认为的那样😂。

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

苹果加入RunLoop的默认的模式是NSDefaultRunLoopMode,当你滑动scrollView的时候runloop将会切换到UITrackingRunLoopMode、在加入的时候设置NSRunLoopCommonModes模式。这样所有模式都可以工作了。

非主线程中创建定时器需要让定时器执行

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      
      NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES];
       [[NSRunLoop currentRunLoop] run];
       
   });

RunLoop用来循环处理响应事件,每个线程都有一个RunLoop,苹果不允许自己创建RunLoop, 而且只有主线程的RunLoop是默认打开的,其他线程的RunLoop如果需要使用就必须手动打开。scheduledTimerWithTimeInterval这个方法创建好NSTimer以后会自动将它添加到当前线程的RunLoop,非主线程中只有调用run方法定时器才能开始工作

相关文章

  • NSTimer之前我真不懂

    NSTimer不就是定时器吗,这个平时经常用的, 这样写之后不做其他处理的同学断点下dealloc方法,你会情不自...

  • 谨防金融诈骗

    之前,我是真的不懂。我是真的不懂要怎么把房子抵押给银行释放一些现金。 我。是。真。的。完。全。不。懂。 那些手续光...

  • NSTimer循环引用的几种解决方案

    前言 在iOS中,NSTimer的使用是非常频繁的,但是NSTimer在使用中需要注意,避免循环引用的问题。之前经...

  • iOS | 不同场景下的定时方法

    在之前的开发随笔小心NSTimer中的循环引用中介绍了NSTimer会因持有目标对象而引起内存泄漏(循环引用)以及...

  • RunLoop入门

    之前在使用NSTimer时,遇到一个问题,NSTimer启动了,但是只会出现一次定时,之后就不起作用。问了同...

  • NSTimer在iOS9之前和iOS9之后释放的问题

    问题描述: 在iOS9及之前通过 _timer = [NSTimer scheduledTimerWithTime...

  • iOS 在子线程中NSTimer的启动和关闭

    之前在项目中遇见了一个问题,在子线程中如何开启NSTimer和取消NSTimer。现在总结一下,当做自己的笔记。 ...

  • iOS 在子线程中NSTimer的启动和关闭

    之前在项目中遇见了一个问题,在子线程中如何开启NSTimer和取消NSTimer。现在总结一下,当做自己的笔记。 ...

  • iOS中的计时器

    一、NSTimer 创建方法 1 NSTimer *timer = [NSTimer scheduledTimer...

  • NSTimer

    创建NSTimer 创建NSTimer的常用方法是: + (NSTimer *)scheduledTimerWit...

网友评论

      本文标题:NSTimer之前我真不懂

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