NSTimer的定義
A timer that fires after a certain time interval has elapsed, sending a specified message to a target object.
簡單理解就是在一定的時間間隔后向指定對象發(fā)送指定消息衰絮。
NSTimer的調(diào)用方式
一募狂、自動加入NSRunLoop
方法名以scheduled開頭的均不需要手動加入NSRunLoop
方法有三個:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
二脖旱、需要手動加入NSRunLoop
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
重點參數(shù)解析
參數(shù):target
The object to which to send the message specified by aSelector when the timer fires.
The timer maintains a strong reference to this object until it (the timer) is invalidated.
當timer開啟時饲嗽,消息的reciver烧给。NSTimer會強引用target直到(the timer) invalidated.
參數(shù):userInfo
Custom user info for the timer.
The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter may be nil.
自定義的額外數(shù)據(jù)。和target一樣喝噪,NSTimer會強引用userInfo直到(the timer) invalidated.
參數(shù):repeats
If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
repeats為YES時,會重復執(zhí)行selector方法直到timer invalidated為止指么。
repeats為NO時, timer會在fires后被置為invalidated酝惧。
注意
:以上說明,repeats為YES時伯诬,要想截止timer晚唇,需要調(diào)用[timer invalidate]方法,而repeats為NO時盗似,系統(tǒng)自動在fires后調(diào)用[timer invalidate]方法哩陕。
- (void)invalidate;
Stops the timer from ever firing again and requests its removal from its run loop.
停止timer,并請求從其運行循環(huán)中刪除它赫舒。
This method is the only way to remove a timer from an [NSRunLoop
](apple-reference-documentation://hclPs8uY7g) object. The NSRunLoop
object removes its strong reference to the timer, either just before the [invalidate
](apple-reference-documentation://hcnIKt1Jb9) method returns or at some later point.
If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.
該方法是從NSRunLoop對象中刪除timer的唯一方法悍及。 NSRunLoop對象刪除其對定時器的強引用,就在invalidate方法返回之前或稍后一點接癌。
如果配置了target和userInfo對象心赶,timer也會刪除其對這些對象的強引用。
常說的循環(huán)引用
1, self強引用timer對象缺猛。
即如下方式:
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerSelector) userInfo:nil repeats:YES];
對象引用圖:
執(zhí)行了invalidate方法后:timer--->target, runloop--->timer 之間的強引用被刪除缨叫。也就不會再有循環(huán)引用了椭符。(當repeats參數(shù)為NO時,timer 觸發(fā)后耻姥,這兩個強引用就自動被刪除了)销钝。
所以如果repeats為YES,并且沒有執(zhí)行invalidate方法時琐簇,形成了循環(huán)引用蒸健,這樣self就無法釋放,從而內(nèi)存泄漏。(有同學在self的dealloc方法中進行[timer invalidate]調(diào)用是無效果的鸽嫂,因為dealloc方法根本不會執(zhí)行纵装。)
2,self沒有強引用timer的情況
執(zhí)行了invalidate方法后:timer--->target, runloop--->timer 之間的強引用被刪除据某。也就不會再有循環(huán)引用了橡娄。(當repeats參數(shù)為NO時,timer 觸發(fā)后癣籽,這兩個強引用就自動被刪除了)挽唉。
所以如果repeats為YES,并且沒有執(zhí)行invalidate方法時筷狼,雖然沒有循環(huán)引用瓶籽,但是runloop強引用timer,timer強引用self(target),由于(主線程中)runloop是程序運行期永久存在的埂材,所以timer無法釋放塑顺,self(target)也就無法釋放,從而內(nèi)存泄漏俏险。
系統(tǒng)方法中有一個方法不會導致self(target)無法釋放严拒。如下:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
該方法沒有參數(shù)targe,所以不會強引用self(target), 所以self(target)可以釋放竖独,但是如果沒有調(diào)用invalidate方法, 所創(chuàng)建的timer也是無法釋放的裤唠,所以依舊會內(nèi)存泄漏。
綜上所訴莹痢,當參數(shù)repeats為YES時种蘸,必須在不使用timer的時候調(diào)用invalidate方法刪除相關強引用,以免內(nèi)存泄漏竞膳。
避免循環(huán)引用的方式:
1航瞭,使用另一個對象,timer強引用newObjc坦辟,newObjc弱引用target沧奴。
github上已經(jīng)有了代碼:YYWeakProxy
對象引用圖:
這樣timer就不再強引用self了,可以在self的dealloc中執(zhí)行invalidate方法釋放timer--->yyweakproxy, runloop----->timer的強引用.
原理:YYWeakProxy中weak屬性target长窄,并重寫forwardingTargetForSelector方法滔吠,直接返回_target.
- (id)forwardingTargetForSelector:(SEL)selector {
return _target;
}
2纲菌,將target設置為類對象。
對象引用圖
雖然依舊有強引用:timer--->NSTimer類對象疮绷,但是由于類對象不需要回收翰舌,所以沒有內(nèi)存泄漏問題。
實現(xiàn)方式:
@interface NSTimer (BlocksSupport)
+(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats;
@end
@implementation NSTimer (BlocksSupport)
+(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(blockInvoke:)
userInfo:[block copy]
repeats:repeats];
}
+(void)blockInvoke:(NSTimer *)timer {
void (^block)() = timer.userinfo;
if(block) {
block();
}
}
@end