NSTimer
相信大家经常都会用到,但是,如果不注意的话,很容易就会造成内存泄漏。
我们先来暂时一个例子:
@interface HomeControllerViewController ()
@property(nonatomic, strong)NSTimer *timer;
@end
@implementation HomeControllerViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor redColor];
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerSchedue:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)timerSchedue:(NSTimer *)timer {
static int i = 0;
NSLog(@"%d",i);
i++;
}
- (void)dealloc {
NSLog(@"已经销毁HomeControllerViewController");
}
@end
如果你从A controller推进一个Homecontroller,然后再点返回,那么造成的现象就是,一直打印1,2,3......不会停止,直到你杀掉进程。并且,NSLog(@"已经销毁HomeControllerViewController");
这句不会打印。
原因分析
为什么会这样呢?
可能有人会猜:既然不走delloc方法,那肯定就是有循环引用在,导致不能释放HomeControllerViewController
,那么,我把timer的target传weakSelf,会不会解决这个问题呢?
经过试验之后,答案是,并不能,还是不走delloc方法。
那么,我们要怎么解决这个问题呢?
我们先来看看文档:
文档上有提到,The receiver retains a Timer。很明显,接收者会强引用timer,那么你穿weakSelf就没什么用了。
如果我们采取一个proxy类,把他的引用断开呢?
这样是否可以解决这个问题呢?按原理来说是应该可以的。我们来尝试下:
我们先创建一个weakProxy类:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface WeakProxy : NSProxy
@property (weak,nonatomic,readonly)id target;
+ (instancetype)proxyWithTarget:(id)target;
- (instancetype)initWithTarget:(id)target;
@end
NS_ASSUME_NONNULL_END
#import "WeakProxy.h"
@implementation WeakProxy
- (instancetype)initWithTarget:(id)target {
_target = target;
return self;
}
+ (instancetype)proxyWithTarget:(id)target {
return [[WeakProxy alloc] initWithTarget:target];
}
- (id)forwardingTargetForSelector:(SEL)selector {
return _target;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
void *null = NULL;
[invocation setReturnValue:&null];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_target respondsToSelector:aSelector];
}
- (BOOL)isEqual:(id)object {
return [_target isEqual:object];
}
- (NSUInteger)hash {
return [_target hash];
}
- (Class)superclass {
return [_target superclass];
}
- (Class)class {
return [_target class];
}
- (BOOL)isKindOfClass:(Class)aClass {
return [_target isKindOfClass:aClass];
}
- (BOOL)isMemberOfClass:(Class)aClass {
return [_target isMemberOfClass:aClass];
}
- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
return [_target conformsToProtocol:aProtocol];
}
- (BOOL)isProxy {
return YES;
}
- (NSString *)description {
return [_target description];
}
- (NSString *)debugDescription {
return [_target debugDescription];
}
@end
这个时候,我们的timer的target要改一下,改成weakproxy:
self.timer = [NSTimer timerWithTimeInterval:1 target:[WeakProxy proxyWithTarget:self] selector:@selector(timerSchedue:) userInfo:nil repeats:YES];
我们运行来看看效果:
果然,delloc的方法执行了,说明,循环引用被打破了。
所以,后面如果用到NSTimer的时候,记得要注意这个问题哦.....