NSTimer的循环引用
2020-05-22 本文已影响0人
冷武橘
一、相互引用
- 1、测试一
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1
target:self selector:@selector(test2) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop]
addTimer:self.timer forMode: NSDefaultRunLoopMode];
}
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
}
测试发现delloc不会被执行,很显然造成了相互引用。NStimer对象会对target对象(这里也就是控制器对象)进行强引用,而self.timer对象又被控制器对象引用,这样就会产生相互引用。
- 2、测试二
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"测试");
}];
[[NSRunLoop mainRunLoop]
addTimer:self.timer forMode: NSDefaultRunLoopMode];
}
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
}
测试发现delloc会执行,不会出现相互引用。
- 3、测试三
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
[self test2];
}];
[[NSRunLoop mainRunLoop]
addTimer:self.timer forMode: NSDefaultRunLoopMode];
}
- (void)test2{
NSLog(@"动态进行");
}
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
}
测试发现delloc不会被执行,同样造成了相互引用。当然很显然,这里是block引用了外部变量造成的。用关键字weak修饰就可以直接解决。
__weak typeof(self)weakself = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakself test2];
}];
二、解决target的相互引用
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1
target:self selector:@selector(test2) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop]
addTimer:self.timer forMode: NSDefaultRunLoopMode];
}
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
}
像block那样的套路,尝试使用weak修饰?不不不,weakself和self地址一样,并不会使NSTimer的target指针置弱。那怎样可行?答案是我们可以建立个自定义target对象,让NSTimer直接强引用它,自定义对象收到消息时转发回去,这样就可以巧妙的解决。
#import <UIKit/UIKit.h>
@interface TestProxy: NSObject
+ (instancetype)proxyWithTarget:(id)target;
@end
@interface TestProxy()
@property(nonatomic,weak)id target;
@end
@implementation TestProxy
+ (instancetype)proxyWithTarget:(id)target{
TestProxy *proxy = [[TestProxy alloc]init];
proxy.target = target;
return proxy;
}
//转发消息回去
- (id)forwardingTargetForSelector:(SEL)aSelector{
return self.target;
}
@end
@implementation SecViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1 target:[TestProxy proxyWithTarget:self] selector:@selector(test2) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop]addTimer:self.timer forMode: NSDefaultRunLoopMode];
}
- (void)test2{
NSLog(@"动态进行");
}
- (void)test{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
}
@end