美文网首页iOS Developer
iOS如何安全而又优雅的使用NSTimer

iOS如何安全而又优雅的使用NSTimer

作者: 温特儿 | 来源:发表于2018-04-15 19:01 被阅读116次

如何安全而又优雅的使用一个NSTimer❓
请往下看👇

使用NSTimer最大的困扰就是在于必须手动释放掉这个被我们创建的timer,然而由于我们的粗心,很大可能忘记了去手动释放

 [self.timer invalidate];
 self.timer = nil;

然而有时候我们却忘记了该在什么时机去释放呢❓

  • 我们很聪明的在dealloc中写下代码,自以为完美无缺的代码
- (void)dealloc {
    [self.timer invalidate];
    self.timer = nil;
}

too young too simple,在dealloc中添加

NSLog(@"======= ViewController dealloc =======");

神奇的发现,居然没有打印,wtf 😱 🤔

于是乎,各种面向搜索引擎,找到了本篇文章~
如何安全而又优雅的使用NSTimer

先来分析下引起循环印用的原因:

假设我们在viewController中写下这样的代码时

[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(increaseLabel) userInfo:nil repeats:YES];

此时 timer->self->timerselftimer释放掉才能释放, 而timer需要等self才能释放,如此一来,造成循环引用,相互等待释放,最后,谁也没有释放掉~

思考中~🤔,bingo🙋
既然dealloc中不能写,那我在viewDidDisappear:中写可以不,试试~

测试中~...

💯看到控制台输出一段字母ViewController dealloc,顿时心情很是愉悦😊
这样的话,这个循环引用的问题不就解决了么~,好吧,就这样了😇

too young too simple~
这样释放掉timer会存在一个潜在的问题,假如当前这个页面的timer是在做一个模块停留计时的动作

假设是这样子的:

用户从A进入B模块,此时B开始计时,
用户从B的首页BHomeViewController进入到BSubViewController时,timer就已经停掉了
再从B返回到A时,此时的计时时间仅仅只有停留在BHomeViewController的时间

这样的结果,肯定不是我们想要的,如果在此基础上,想要得到一个较准确的计时时间,我们就必须加上一些(多余的)标记代码,来记录需要的一些状态,想一想,axb,真够low的👎

于是乎,各种面向搜索引擎,还是找到了本篇文章~
如何安全而又优雅的使用NSTimer
此处直接贴代码,代码不复杂,相信各位看官都看的懂

NSTimer+Secure.h

#import <Foundation/Foundation.h>

@interface NSTimer (Secure)
/// 默认 repeats
+ (instancetype _Nullable)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id _Nonnull)aTarget selector:(SEL _Nonnull)aSelector;
+ (instancetype _Nullable)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id _Nonnull)aTarget selector:(SEL _Nonnull)aSelector repeats:(BOOL)yesOrNo;

/// 默认 repeats
+ (instancetype _Nullable)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id _Nonnull)aTarget block:(void (^_Nonnull)(void))block;
+ (instancetype _Nullable)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id _Nonnull)aTarget repeats:(BOOL)yesOrNo block:(void (^_Nonnull)(void))block;
@end

NSTimer+Secure.m

#import "NSTimer+Secure.h"

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

@property (nonatomic, copy) void(^block)(void);
@end

@implementation SeTimerTarget
- (void)seTimerTargetAction:(NSTimer *)timer {
    if (self.target) {
        IMP imp = [self.target methodForSelector:self.selector];
        void (*func)(id, SEL, NSTimer*) = (void *)imp;
        func(self.target, self.selector, timer);
    }
    else {
        [self.timer invalidate];
        self.timer = nil;
    }
}

- (void)seTimerBlockAction:(NSTimer *)timer {
    if (self.target && self.block) {
        self.block();
    }
    else {
        [self.timer invalidate];
        self.timer = nil;
    }
}

- (void)dealloc {
    NSLog(@"==== timer dealloc ====");
}
@end

@implementation NSTimer (Secure)

+ (instancetype)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector {
    return [self seScheduledTimerWithTimeInterval:ti target:aTarget selector:aSelector repeats:YES];
}

+ (instancetype)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector repeats:(BOOL)yesOrNo {
    SeTimerTarget *timerTarget = [[SeTimerTarget alloc] init];
    NSTimer *timer = [NSTimer timerWithTimeInterval:ti target:timerTarget selector:@selector(seTimerTargetAction:) userInfo:nil repeats:yesOrNo];
    timerTarget.target = aTarget;
    timerTarget.selector = aSelector;
    timerTarget.timer = timer;
    
    [[NSRunLoop mainRunLoop] addTimer:timerTarget.timer forMode:NSRunLoopCommonModes];
    return timerTarget.timer;
}

+ (instancetype)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget block:(void (^)(void))block {
    return [self seScheduledTimerWithTimeInterval:ti target:aTarget repeats:YES block:block];
}

+ (instancetype)seScheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget repeats:(BOOL)yesOrNo block:(void (^)(void))block {
    SeTimerTarget *timerTarget = [[SeTimerTarget alloc] init];
    timerTarget.block = block;
    timerTarget.target = aTarget;
    NSTimer *timer = [NSTimer timerWithTimeInterval:ti target:timerTarget selector:@selector(seTimerBlockAction:) userInfo:nil repeats:yesOrNo];
    timerTarget.timer = timer;
    
    [[NSRunLoop mainRunLoop] addTimer:timerTarget.timer forMode:NSRunLoopCommonModes];
    return timerTarget.timer;
}
@end

也可以在GitHub上去下载我的测试demo
从上面NSTimer+Secure.h中可以看出,作者提供了两种NSTimer事件回调方式,target-actionblock

在使用中,也是很方便

 [NSTimer seScheduledTimerWithTimeInterval:0.1 target:self selector:@selector(timerAction) repeats:YES];

或者

 __weak typeof(self) weakSelf = self;
[NSTimer seScheduledTimerWithTimeInterval:0.1 target:self block:^{
    [weakSelf timerAction];
}];

这样使用NSTimer,就完美解决以上所说的问题,也无需关心timerviewController谁先释放的问题
安全而又优雅的code🙋

demo地址
以上是本篇分享的所有内容,希望对你有所帮助✌️~

相关文章

  • iOS如何安全而又优雅的使用NSTimer

    如何安全而又优雅的使用一个NSTimer❓请往下看? 使用NSTimer最大的困扰就是在于必须手动释放掉这个被我们...

  • ios中循环引用问题

    ios中循环引用问题 NO1: NSTimer 问题:当你创建使用NSTimer的时候,NSTimer会默...

  • 无标题文章

    iOS NSTimer使用详解-开启、关闭、移除 定时器定时器详解ios定时器关闭定时器NSTimer 1、要使用...

  • NSTimer的循环引用

    NSTimer基本使用 NSTimer与RunLoop NSTimer 循环引用的问题 如何在子线程使用NSTim...

  • iOS NSTimer使用小结:

    NSTimer 是iOS开发中常用的定时器, NSTimer的使用常见的有两种方式: 1. [NSTimer t...

  • NSTimer循环引用的问题

    问题简介 在iOS中,NSTimer的使用非常频繁,但是NSTimer在使用中需要注意,避免循环引用的问题: 由于...

  • NSTimer

    深入NSTimer(iOS)iOS 中的 NSTimer关于NSRunLoop和NSTimer的深入理解

  • iOS延迟执行的三种方式

    1.NSTimer NSTimer 是iOS开发工作中经常会使用到,充当着定时器的作用。NSTimer不会阻塞主线...

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

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

  • 多线程与NSTimer

    1.Ios主线程,也称UI线程,在主线程中使用NSTimer,runloop是自动开启的,(如果NSTimer当前...

网友评论

    本文标题:iOS如何安全而又优雅的使用NSTimer

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