iOS 如何解決NSTimer的循環(huán)引用造成界面不被釋放

方法一:

在我剛接觸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

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

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

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