pop timer所在的控制器,会发现timer还在运行,因为tiemr创建加入了runloop中,如果不主动的调用 invalidate 方法,timer是不会被释放掉的,根本停不下来。
重写dealloc方法,发现pop不走dealloc方法,所以在dealloc中调用invalidate也是没有用的。
控制器要释放掉,则需释放掉所有的实例变量,释放timer,但是释放掉timer则必须释放掉它持有target,由于初始化时timer的target设置为了self,就是当前控制器,所以造成了循环引用,内存泄漏了。
解决办法就是:解除timer对self的强引用,把target替换成单独的对象,再利用消息转发/消息传递使得单独对象中的timer的SEL方法在控制器中实现
方法一:使用NSProxy解决NSTimer内存泄漏问题,利用消息转发来断开NSTimer对象与视图之间的引用关系
@interface TimerProxy : NSProxy
- (instancetype)initWithTarget:(id)target;
@end
#import "TimerProxy.h"
@interface TimerProxy ()
@property (nonatomic,weak)id target;
@end
@implementation TimerProxy
- (instancetype)initWithTarget:(id)target {
self.target = target;
return self;
}
/*
这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
/*
将消息转发给其他对象,这里转发给控制器
*/
- (void)forwardInvocation:(NSInvocation *)invocation {
SEL sel=[invocation selector];
if ([self.target respondsToSelector:sel]) {
[invocation invokeWithTarget:self.target];
}
}
使用方法:
- (void)proxy {
TimerProxy *proxy=[[TimerProxy alloc]initWithTarget:self];
timer =[NSTimer timerWithTimeInterval:1 target:proxy selector:@selector(timeAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)timeAction {
time++;
self.timeLabel.text=[NSString stringWithFormat:@"%d",time];
NSLog(@"time %d",time);
}
- (void)dealloc {
[timer invalidate];
}
方法二:使用 消息传递 解决NSTimer内存泄漏问题
创建一个对象,让NSTimer对这个对象进行强引用,而不对控制器进行强引用
@interface WeakTimeObject : NSObject
+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval target:(id)target selector:(SEL)sel userInfo:(id)userInfo repeats:(BOOL)isRepeats;
@end
@interface WeakTimeObject ()
@property (nonatomic,weak) id target;
@property (nonatomic,assign)SEL selector;
@end
@implementation WeakTimeObject
+ (NSTimer *)weakScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval target:(id)target selector:(SEL)sel userInfo:(id)userInfo repeats:(BOOL)isRepeats {
WeakTimeObject *objc=[[WeakTimeObject alloc]init];
objc.target = target;
objc.selector = sel;
//Nstimer 对 WeakTimeObject 对象 强引用
return [NSTimer scheduledTimerWithTimeInterval:timeInterval target:objc selector:@selector(timeAction:) userInfo:userInfo repeats:isRepeats];
}
//消息传递,在 self.target 运行 self.selector 方法
- (void)timeAction:(id)info {
[self.target performSelector:self.selector withObject:info];
}
使用方法
- (void)objc {
timer = [WeakTimeObject weakScheduledTimerWithTimeInterval:1.0 target:self selector:@selector(update) userInfo:nil repeats:YES];
}
- (void)update {
time++;
self.timeLabel.text=[NSString stringWithFormat:@"%d",time];
NSLog(@"time %d",time);
}
- (void)dealloc {
[timer invalidate];
}