場景:一個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)引用:

想要打破這種循環(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 還有好多用處,建議大家在深度學一學.
大概就這些了,有哪些寫的不好的歡迎大家指正.多謝!