方法一:
在我剛接觸NSTimer的時(shí)候,為了解決NSTimer的循環(huán)引用,我會在viewWillDisappear中:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.timer invalidate];
self.timer = nil;
}
但是問題來了,如果是push而不是pop的話,再次進(jìn)去VC,這個(gè)timer需要重新創(chuàng)建
后來我找到了didMoveToParentViewController:(UIViewController *)parent 這個(gè)方法,當(dāng)push的時(shí)候parent是有值的,當(dāng)pop的時(shí)候是沒有值的,所以就有了:
- (void)didMoveToParentViewController:(UIViewController *)parent
{
NSLog(@"didMoveToParentViewController %@",parent);
if (parent == nil) {
[self.timer invalidate];
self.timer = nil;
}
}
雖然這個(gè)方法有點(diǎn)low,不過也算是解決了
方法二:
因?yàn)槭荖STimer的Target是綁定的,那么我們是否可以綁定另外一個(gè)target呢?而這個(gè)target的回收又是由self去管理的:
新建一個(gè)id類型的屬性:
@property (nonatomic,strong)id target;
NSTimer綁定Target修改為:
self.target = [NSObject new];
class_addMethod([self.target class], @selector(fire), (IMP)cMethod, "v@:");
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self.target selector:@selector(fire) userInfo:nil repeats:YES];
新建一個(gè)c函數(shù)
void cMethod(id self,SEL _cmd){
NSLog(@"CMethod %@",NSStringFromClass([self class]));
}
這時(shí)候我們依然在dealloc里面去銷毀timer
運(yùn)行項(xiàng)目發(fā)現(xiàn)dealloc執(zhí)行了,timer銷毀了
在這里需要解釋一下上面的class_addMethod的實(shí)際作用,看似是給target增加了一個(gè)方法,但是微妙之處就在于timer的執(zhí)行是在cMethod里面執(zhí)行的,而不是原來的fire函數(shù).分析一下:在沒有使用自定義的target之前,fire函數(shù)的IMP是指向fire的這是毋庸置疑的,而使用class_addMethod之后,相當(dāng)于重新指定了fire的IMP指針,讓他指向了cMethod,造成的結(jié)果就是fire擺在那里,有點(diǎn)礙眼.
作為強(qiáng)迫癥的我,我要想辦法去掉這個(gè)礙眼的又不執(zhí)行的fire函數(shù),既然在class_addMethod中需要一個(gè)函數(shù)的IMP,那么我們可以獲取fire的IMP就可以了,不用新建一個(gè)c函數(shù)了,那么就有了如下的優(yōu)化:
self.target = [NSObject new];
Method method = class_getInstanceMethod([self class], @selector(fire));
class_addMethod([self.target class], @selector(fire), method_getImplementation(method), "v@:");
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self.target selector:@selector(fire) userInfo:nil repeats:YES];
運(yùn)行發(fā)現(xiàn)fire確實(shí)執(zhí)行了,我終于把cMethod刪掉了,清爽.
方法三:
同樣是需要解決target和VC的綁定關(guān)系,方法三我們得用到NSProxy這個(gè)類,這個(gè)類負(fù)責(zé)消息轉(zhuǎn)發(fā)到真正的代理類
新建一個(gè)繼承與NSProxy的類WPProxy
//NSProxy消息轉(zhuǎn)發(fā)到真正的代理類
@interface WPProxy : NSProxy
@property (nonatomic,weak)id target;
@end
WPProxy.m中:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
//獲得target里面sel的方法簽名
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
//轉(zhuǎn)發(fā)給自己進(jìn)行處理
[invocation setTarget:self.target];
[invocation invoke];
}
在VC中我們就不用用在方法二中的runtime的方法了:
self.proxy = [WPProxy alloc];
self.proxy.target = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self.proxy selector:@selector(fire) userInfo:nil repeats:YES];
運(yùn)行同樣是可以解決NSTimer和VC的循環(huán)引用問題.
最后附上demo地址:https://github.com/gnaw9258wp/WPTimer.git