NSTimer的循環(huán)引用問題解決

場景:一個VC想引用一個帶著NStimer的View.但是一般的情況下我們要在VC的dealloc中還要銷毀View中的timer.如果不這樣做view就不會釋放,引起內(nèi)存泄漏.甚至crash.如果多個VC引用這個View,那樣每個VC的dealloc都要管理這個View的timer豈不是很麻煩.這時候我們肯定是想讓View自己處理自己的timer.

1.首先我們說一下NStimer循環(huán)引用的情況.我們直接看代碼.

self.timer = [NSTimer timerWithTimeInterval:self.animationDuration target:self selector:@selector(switchPage) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

這樣就會造成一個這樣的循環(huán)引用:

圖1

想要打破這種循環(huán)只有兩種辦法.

1.控制器不再引用定時器

2.定時器不再保留當前控制器

第一種是不可能的了,那就只能是第二種了.接下來我們看看怎么打破這種循環(huán).

首先我們要認識一下NSProxy這個類.

NSProxy是一個抽象類,它為一些表現(xiàn)的像是其它對象替身或者并不存在的對象定義一套API。一般的,發(fā)送給代理的消息被轉(zhuǎn)發(fā)給一個真實的對象或者代理本身load(或者將本身轉(zhuǎn)換成)一個真實的對象。NSProxy的基類可以被用來透明的轉(zhuǎn)發(fā)消息或者耗費巨大的對象的lazy初始化。NSProxy實現(xiàn)了包括NSObject協(xié)議在內(nèi)基類所需的基礎方法,但是作為一個虛擬的基類并沒有提供初始化的方法。它接收到任何自己沒有定義的方法他都會產(chǎn)生一個異常,所以一個實際的子類必須提供一個初始化方法或者創(chuàng)建方法,并且重載forwardInvocation:方法和methodSignatureForSelector:方法來處理自己沒有實現(xiàn)的消息。一個子類的forwardInvocation:實現(xiàn)應該采取所有措施來處理invocation,比如轉(zhuǎn)發(fā)網(wǎng)絡消息,或者加載一個真實的對象,并把invocation轉(zhuǎn)發(fā)給他。methodSignatureForSelector:需要為給定消息提供參數(shù)類型信息,子類的實現(xiàn)應該有能力決定他應該轉(zhuǎn)發(fā)消息的參數(shù)類型,并構造相對應的NSMethodSignature對象。詳細信息可以查看NSDistantObject,NSInvocation, and NSMethodSignature的類型說明。

說白了就是做一個消息的轉(zhuǎn)發(fā).

我們要做的就是實例一個NSProxy的subclass.讓NSTimer定時中的方法由NSProxy的subclass轉(zhuǎn)發(fā)給View執(zhí)行.但是NStimer持有的卻不是View.這樣就不會循環(huán)引用的.

首先我們構建NSProxy的subclass.命名為VMRProxy.

VMRProxy.h中的代碼

#import@interface VMRProxy : NSProxy

@property (nonatomic, weak) id target;

@end

VMRProxy.m中的代碼

#import "VMRProxy.h"

@implementation VMRProxy

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {

return [self.target methodSignatureForSelector:sel];

}

- (void)forwardInvocation:(NSInvocation *)invocation {

[invocation invokeWithTarget:self.target];

}

@end

NSProxy的subclass必須重寫接兩個方法.文檔上是這樣說的:

A concrete subclass must therefore provide an initialization or creation method and override theforwardInvocation:andmethodSignatureForSelector:methods to handle messages that it doesn’t implement itself.

接下來我們看看View中是怎樣實現(xiàn)的:

在View的.m文件中構建屬性:

@property (nonatomic, strong) NSTimer *timer;

@property (nonatomic, strong) VMRProxy *proxy;

// 實例proxy 并指定執(zhí)行方法的對象

- (instancetype)initWithFrame:(CGRect)frame {

self = [super initWithFrame:frame];

if (self) {

self.proxy = [VMRProxy alloc];

self.proxy.target = self;

}

return self;

}

// 使用定時器

self.timer = [NSTimer timerWithTimeInterval:self.animationDuration target:self.proxy selector:@selector(switchPage) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

這塊代碼可以放在initWithFrame:方法中.也可以寫在自定義的方法中,在initWithFrame:
中引用自定義的方法,畢竟這樣看著比較清晰吧.(我的代碼習慣會這么做)

這樣View的dealloc方法就會調(diào)用了.然后在dealloc中做這樣的操作.

- (void)dealloc {

[self.timer invalidate];

self.timer = nil;

}

一個能自己銷毀定時器的view就做成了.

注意事項:

1.VMRProxy 一定要定義成全局的屬性.要是局部的話,出了}就會銷毀了.

2.NSProxy 還有好多用處,建議大家在深度學一學.

大概就這些了,有哪些寫的不好的歡迎大家指正.多謝!

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

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

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