為什么要寫這篇破事水
這些代碼來自于年初某短視頻巨廠面試時(shí)候的即興表演,
當(dāng)時(shí)寫的是偽代碼,現(xiàn)在把他補(bǔ)完整
面試官提出來的要求如下:
NSTimer用起來有很多注意點(diǎn),不方便,要求實(shí)現(xiàn)一個(gè)
+ (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
1.解決循環(huán)引用
2.aTarget釋放時(shí)自動(dòng)invalidate
3.多個(gè)timer可以同時(shí)存在
那他滿意了嗎
看他笑的挺滿意的,那一輪面試也確實(shí)過了,可惜二面沒表現(xiàn)好……我懷疑是被白剽思路(不可能的你想太多了)
那在哪里可以拿到demo呢?
那怎么實(shí)現(xiàn)
當(dāng)時(shí)思考了2分鐘拿出以下方案,大致就是用了備用接收者和完整消息轉(zhuǎn)發(fā)來完成這個(gè)方法,貼一點(diǎn)關(guān)鍵代碼,完整demo代碼在上面的github里
- NSTimer+Easy方法
+ (NSTimer *)clc_scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo {
CLCTimerObject *obj = [[CLCTimerObject alloc] init];
obj.target = aTarget; //weak
obj.myTimer = [NSTimer scheduledTimerWithTimeInterval:ti target:obj selector:aSelector userInfo:userInfo repeats:yesOrNo];
//timer 持有obj,obj弱引用aTarget,obj同時(shí)也負(fù)責(zé)處理aTarget釋放時(shí)timer的invalidate
return obj.myTimer;
}
2.CLCTimerObj屬性
//一個(gè)持有aTarget,一個(gè)持有timer,都是弱引用,反正timer啟動(dòng)了就會(huì)被掛住
@property (nonatomic, weak) NSObject *target;
@property (nonatomic, weak) NSTimer *myTimer;
3.CLCTimerObj實(shí)現(xiàn)
//用runtime的備用接收者機(jī)制去判斷aTarget釋放了沒
- (id)forwardingTargetForSelector:(SEL)aSelector {
//target還沒釋放 --> 返回的是self.target對象
//target被釋放了 --> 返回的是nil,進(jìn)入下一步消息轉(zhuǎn)發(fā)
NSLog(@"send to aTarget");
return self.target;
}
//用完整消息轉(zhuǎn)發(fā)兜底,上一步返回nil時(shí)就invalidate掉timer
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
NSLog(@"return Signature");
return [NSMethodSignature signatureWithObjCTypes:"v@:@@"]; //隨便簽,無所謂啦
}
- (void)forwardInvocation:(NSInvocation *)invocation {
NSLog(@"call forwordInvocation");
[self _invalidateTimer];
}
- (void)_invalidateTimer {
if (self.myTimer) {
[self.myTimer invalidate];
self.myTimer = nil;
}
}
利弊分析
好處
1.防止發(fā)生循環(huán)引用、timer沒有好好手動(dòng)invalidate等問題,傻瓜式timer(只是做保護(hù),還是建議手寫invalidate)
2.一次性倒計(jì)時(shí)View(比如543210開始直播)不需要把timer掛住當(dāng)屬性,時(shí)間到了自己removeFromSuperview引用計(jì)數(shù)歸零變成nil就行了,不需要管timer,他是自動(dòng)的
3.自動(dòng)停止能確保NSTimer的invalidate和init在同一個(gè)線程里
孬處
- 維護(hù)起來不容易閱讀(“我艸這timer啥時(shí)候釋放的?”)