在做限時支付,驗證碼發(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)不會釋放。