問題描述:
用NSTimer來實現(xiàn)每隔一定時間執(zhí)行制定的任務,例如最常見的廣告輪播圖。如果我們在 timerWithTimeInterval:1 target:self 中指定target為當前控制器,控制器則會被timer強引用,而控制器對timer也是強引用的。一般,我們終止定時器往往在界面銷毀時,即dealloc方法中寫 [_timer invalidate];。基于上面的分析,由于循環(huán)引用的存在,控制器永遠也不會走dealloc方法,定時器會一直執(zhí)行方法,造成內(nèi)存泄露。
解決方案:利用消息轉(zhuǎn)發(fā)來斷開NSTimer對象與視圖之間的引用關系。初始化NSTimer時把觸發(fā)事件的target替換成一個單獨的對象,然后這個對象中NSTimer的SEL方法觸發(fā)時讓這個方法在當前的視圖self中實現(xiàn)。
背景知識:
NSProxy:NSProxy 是一個抽象類,它接收到任何自己沒有定義的方法他都會產(chǎn)生一個異常,所以一個實際的子類必須提供一個初始化方法或者創(chuàng)建方法,并且重載forwardInvocation:方法和methodSignatureForSelector:方法來處理自己沒有實現(xiàn)的消息。
從類名來看是代理類,專門負責代理對象轉(zhuǎn)發(fā)消息的。相比NSObject類來說NSProxy更輕量級,通過NSProxy可以幫助Objective-C間接的實現(xiàn)多重繼承的功能。
代碼:
#import <Foundation/Foundation.h>
@interface XZHProxy : NSProxy
/**
* 代理的對象
*/
@property (nonatomic,weak)id obj;
@end
#import "XZHProxy.h"
@implementation XZHProxy
/**
這個函數(shù)讓重載方有機會拋出一個函數(shù)的簽名,再由后面的forwardInvocation:去執(zhí)行
為給定消息提供參數(shù)類型信息
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSMethodSignature *sig = nil;
sig = [self.obj methodSignatureForSelector:aSelector];
return sig;
}
/**
* NSInvocation封裝了NSMethodSignature,通過invokeWithTarget方法將消息轉(zhuǎn)發(fā)給其他對象.這里轉(zhuǎn)發(fā)給控制器執(zhí)行。
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:self.obj];
}
@end
#import "ViewController.h"
#import "XZHProxy.h"
@interface ViewController ()
/**
*代理
*/
@property (nonatomic,strong)XZHProxy *proxy;
/**
* 定時器
*/
@property (nonatomic,strong)NSTimer *timer;
/**
* 計數(shù)
*/
@property (nonatomic,assign)NSInteger count;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.proxy = [XZHProxy alloc];
//作為當前控制器的代理
self.proxy.obj = self;
//target為代理
self.timer = [NSTimer timerWithTimeInterval:1 target:self.proxy selector:@selector(timerEvent) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)timerEvent{
NSLog(@"%zd",self.count++);
}
- (void)dealloc{
NSLog(@"----dealloc");
//釋放計時器
[self.timer invalidate];
}