NSTimer循環引用踩坑紀實

在iOS開發中首頁展示廣告欄,并定時驅動的展示效果很常見,就目前我經手的項目來看,多數有這種展示,這種是需要用定時器驅動的,然基本沒有人考慮到NSTimer使用造成的循環引用問題,特別是對于一些需要切換框架的App,頻繁的切換框架,造成頻繁的創建新框架,原框架由于和NSTimer相互引用無法銷毀,造成內存增長,閑話不多說,進入正題:

一般情況下創建定時器的方法:

- (NSTimer *)verifyTimer{

    if (!_verifyTimer) {//立刻執行
        _verifyTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(verifyTimerAction:) userInfo:nil repeats:YES];
    }
    return _verifyTimer;
}
self.timer = [NSTimer timerWithTimeInterval:3.0f target:self selector:@selector(timer:) userInfo:nil repeats:YES]
//加入runloop后立即執行
[[NSRunLoop currentRunLoop] addTimer:self.bannerTimer forMode:NSRunLoopCommonModes];

在這里不對每個參數過多介紹,就上邊的兩種創建方式看,都存在循環引用的情況
一般我們在使用NSTimer時都會把它當做一個屬性來使用,NSTimer被self引用,在重復調用方法時,將self作為target,這時NSTimer會獲取保留self,此時就會造成循環引用使self和NSTimer都不能銷毀,當然我們可以在銷毀self之前使用以下兩句銷毀NSTimer

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

然不是所有開發者都這么注意此處的循環引用問題,廢話不多說,直接上代碼,讓我們對使用方法做一下處理

__weak __typeof(&*self)ws = self;
if (self.bannerTimer == nil) {//此處必須加判斷,若是重復創建加入runloop,則會同時進行若干個timer,廣告切換則會加快
self.bannerTimer = [NSTimer  block_TimerWithTimeInterval:3.0f block:^{   
       [ws nextPageAction];
} repeats:YES];
    
[[NSRunLoop currentRunLoop] addTimer:self.bannerTimer forMode:NSRunLoopCommonModes];
}

使用上述方法需要創建NSTimer的分類

#import <Foundation/Foundation.h>

@interface NSTimer (TimerBlockSuport)

/**
 分類解決NSTimer在使用時造成的循環引用的問題

 @param interval 間隔時間
 @param block    回調
 @param reqeats  是否立刻執行

 @return 返回NSTimer實體
 */
+ (NSTimer *)block_TimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)reqeats;

@end

#import "NSTimer+TimerBlockSuport.h"

@implementation NSTimer (TimerBlockSuport)

+ (NSTimer *)block_TimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)())block repeats:(BOOL)reqeats{
    
    return [self timerWithTimeInterval:interval target:self selector:@selector(blockinvoke:) userInfo:[block copy] repeats:reqeats];
    
}

+ (void)blockinvoke:(NSTimer *)timer{

    void (^block)() = timer.userInfo;
    if (block) {
        block();
    }
    
}

上述創建方式我們方法的調用者是NSTImer自己,只是NSTimer捕獲了參數block,若是block中也是用self調用方法的話,也會造成循環引用,我們這里將self弱引用,這樣NSTimer不會捕獲self的強引用,巧妙的避開了循環引用。

小坑:

在使用時遇到一個小問題,在使用定時器時,我沒有判斷屬性NSTimer是否為nil,導致多次對屬性NSTImer賦值,造成runloop中有多個調用同一個方法的NSTimer在跑,導致重復執行時間間隔變小的問題,在這提醒一下,使用時切記判斷是否為nil

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容