NSTimer保留對象導(dǎo)致內(nèi)存泄漏

在做限時支付,驗證碼發(fā)送之類的功能時經(jīng)常需要使用NTimer來做定時器,但是NSTimer在invalidate之前會保留持有它target對象,導(dǎo)致targtet對象無法釋放,即使在delloc中:
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
}
也無法釋放,因為target對象一直被引用就不會進(jìn)入delloc方法。

  • 所以最基本的方法就是在target對象需要釋放之前手動去控制執(zhí)行計時器的invalidate方法,但有時計時器的invalidate方法并不是完全能控制調(diào)用的,因為要確定target對象在最后一個引用釋放之前調(diào)用計時器的invalidate方法,這通過代碼無法完全檢測出來。在查看了《Effective Objective-C 2.0:編寫高質(zhì)量iOS與OS X代碼的52個有效方法》之后得知可以用塊來打破“保留環(huán)”。

首先,在分類中添加下面這段代碼:

#import <Foundation/Foundation.h>

@interface NSTimer (Addtions)

/**
 *  計時器動作
 *
 *  @param interval 時間
 *  @param block    事件
 *  @param repeats  是否重復(fù)
 */
+ (NSTimer *)sf_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;


@end
#import "NSTimer+Addtions.h"

@implementation NSTimer(Addtions)

/**
 *  計時器動作
 *
 *  @param interval 時間
 *  @param block    事件
 *  @param repeats  是否重復(fù)
 */
+ (NSTimer *)sf_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                    block:(void(^)())block
                                  repeats:(BOOL)repeats{
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self
                                       selector:@selector(private_blockinvoke:)
                                       userInfo:[block copy]
                                        repeats:repeats];
}


+ (void)private_blockinvoke:(NSTimer *)timer{
    void (^block)() = timer.userInfo;
    if (block) {
        block();
    }
}

@end
  • 在代碼中,把要執(zhí)行的事件封裝成“塊”,通過userInfo參數(shù)傳出去(uerInfo可用來存放“不透明值”),那么只要計時器有效,就會一直保留著它。這里有個地方要注意一下,傳遞block時需要copy一下,把block拷貝到“堆”上,因為定義塊的時候,所占的內(nèi)存區(qū)域是分配在棧中的,這說明,塊只在定義它的那個范圍內(nèi)有效,通過拷貝就可以把塊從棧復(fù)制到堆上,塊就可以在定義它的范圍之外使用。

  • 現(xiàn)在計時器的target是NSTimer類對象,而NSTimer類對象是個單例,是否持有它都無所謂了。


現(xiàn)在我們直接使用這個函數(shù)去創(chuàng)建計時器:

    self.timer = [NSTimer sf_scheduledTimerWithTimeInterval:1.0
                                                      block:^{
                                                          [self timerAction];
                                                      } repeats:YES];

這時依然會形成“保留環(huán)”,因為block保留了self對象,self又持有timer屬性。所以在調(diào)用函數(shù)之前要使用weak引用來打破它:

    __weak RegistVC *weakSelf = self;
    self.timer = [NSTimer sf_scheduledTimerWithTimeInterval:1.0
                                                      block:^{
                                                          RegistVC *strongSelf = weakSelf;
                                                          [strongSelf timerAction];
                                                      } repeats:YES];

到這里,問題就算解決了,當(dāng)self的最后一個引用將其釋放的時候,self就會被釋放了,如果忘記在delloc中調(diào)用計時器的invalidate方法,則weakSelf會變?yōu)閚il。在調(diào)試過程中也能看到對象delloc了,而不是再持有一段時間甚至永遠(yuǎn)不會釋放。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容