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

如何安全而又優雅的使用一個NSTimer❓ 請往下看👇git

使用NSTimer最大的困擾就是在於必須手動釋放掉這個被咱們建立的timer,然而因爲咱們的粗心,很大可能忘記了去手動釋放github

[self.timer invalidate];
 self.timer = nil;
複製代碼

然而有時候咱們卻忘記了該在什麼時機去釋放呢❓安全

  • 咱們很聰明的在dealloc中寫下代碼,自覺得天衣無縫的代碼
- (void)dealloc {
    [self.timer invalidate];
    self.timer = nil;
}
複製代碼

too young too simple,在dealloc中添加bash

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

神奇的發現,竟然沒有打印,wtf 😱 🤔oop

因而乎,各類面向搜索引擎,找到了本篇文章~ 如何安全而又優雅的使用NSTimer測試

先來分析下引發循環印用的緣由:ui

假設咱們在viewController中寫下這樣的代碼時搜索引擎

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

此時 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地址 以上是本篇分享的全部內容,但願對你有所幫助✌️~

相關文章
相關標籤/搜索