您的位置:首页 > 移动开发 > IOS开发

ios - NSTimer中target的self是强引用问题

2016-05-05 10:45 393 查看
当控制器ViewController跳转进入控制器OneViewController中的时候开启定时器,让定时器每隔一段时间打印一次,当OneViewController dismiss的时候,控制器并没有被销毁.然而定时器的timer invalidate 在dealloc中已经写了.

如果没有定时器,则OneViewController可以正常销毁.

原因在于下图:循环引用



控制器ViewController跳转进入OneViewController中开启定时器

#import "OneViewController.h"

@interface OneViewController ()

@property (nonatomic, strong) NSTimer *timer;

@end

@implementation OneViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

[self dismissViewControllerAnimated:YES completion:nil];
}

-(void)viewDidLoad{

[super viewDidLoad];
self.view.backgroundColor = [UIColor orangeColor];

/**
1.__weak typeof(self) weakSelf = self; 不能解决

*/

//开启定时器
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(testTimerDeallo) userInfo:nil repeats:YES];
}

/** 方法一直执行 */
-(void)testTimerDeallo{

NSLog(@"-----");
}


当开启定时器以后,testTimerDeallo方法一直执行,即使dismiss此控制器以后,也是一直在打印,而且dealloc方法不会执行.循环引用造成了内存泄露,控制器不会被释放.

/** 开启定时器以后控制器不能被销毁,此方法不会被调用 */
-(void)dealloc{

NSLog(@"xiaohui");
[self.timer invalidate];
}

@end


解决办法: 由于循环引用的起因是target,则可以包装一个target,让target是另一个对象,而不是ViewController即可.

1.创建一个集成NSObject的分类TimerWeakTarget,创建类方法---开启定时器的方法

#import <Foundation/Foundation.h>

@interface TimerWeakTarget : NSObject

@property (nonatomic, assign) SEL selector;
@property (nonatomic, weak) NSTimer *timer;
@property (nonatomic, weak) id target;

/**
1.重写开启定时器方法,在内部对target进行替换,换成本类(TimerWeakTarget)的对象即可
2.不会造成循环引用了,原控制器OneViewController属性有timer对timer强应用,timer内部对self强引用,但是self在此方法内部被替换成了本类的对象(TimerWeakTarget *),而本类的对象不会对OneViewController强引用,则不会造成循环引用,也就不会造成内存泄露
*/
+ (NSTimer *) scheduledTimerWithTimeInterval:(NSTimeInterval)interval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats;

@end


TimerWeakTarget.m文件中

在下面我们封装的类的方法中,我们将开启定时器的方法 [NSTimer scheduledTimerWithTimeInterval:interval target:timer selector:@selector(fire:) userInfo:userInfo repeats:repeats];中的target换掉了,换成了 本类的对象,timer.在OneViewController中开启定时器的时候直接调用这个类方法,就不会造成循环引用.看图



#import "TimerWeakTarget.h"

@implementation TimerWeakTarget

+ (NSTimer *) scheduledTimerWithTimeInterval:(NSTimeInterval)interval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats{

TimerWeakTarget * timer = [TimerWeakTarget new];
timer.target = aTarget;
timer.selector = aSelector;
//-------------------------------------------------------------此处的target已经被换掉了不是原来的VIewController而是TimerWeakTarget类的对象timer
timer.timer = [NSTimer scheduledTimerWithTimeInterval:interval target:timer selector:@selector(fire:) userInfo:userInfo repeats:repeats];
return timer.timer;
}

-(void)fire:(NSTimer *)timer{

if (self.target) {
[self.target performSelector:self.selector withObject:timer.userInfo];
} else {

[self.timer invalidate];
}
}

@end


控制器dismiss以后可以正常被销毁.问题解决.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: