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

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

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

原因在于下图:循环引用

ios - NSTimer中target的self是强引用问题_第1张图片

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

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

   ///开启定时器 

self.timer = [NSTimerscheduledTimerWithTimeInterval:2.0target:selfselector:@selector(testTimerDeallo) userInfo:nilrepeats:YES];

}

/** 方法一直执行 */

-(void)testTimerDeallo{

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

}

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

/** 开启定时器以后控制器不能被销毁,此方法不会被调用 */

-(void)dealloc{

NSLog(@"xiaohui");   

 [self.timer invalidate];

}

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

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

@interface TimerWeakTarget : NSObject

@property (nonatomic, weak) id target;

@property (nonatomic, assign) SEL selector;

@property (nonatomic, weak) NSTimer* timer;

@end

@implementation TimerWeakTarget

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

    if(self.target) {

   [self.target performSelector:self.selector withObject:timer.userInfo afterDelay:0.0f];

    } else {

        [self.timer invalidate];

    }

}

@end

二:下一步

1.重写开启定时器方法,在内部对target进行替换,换成本类(TimerWeakTarget)的对象即可

2.不会造成循环引用了,原控制器OneViewController属性有timer对timer强应用,timer内部对self强引用,但是self在此方法内部被替换成了本类的对象(TimerWeakTarget *),而本类的对象不会对OneViewController强引用,则不会造成循环引用,也就不会造成内存泄露

3.在TimerWeakTarget.m文件中重写NSTimer的方法类在下面我们封装的类的方法中,我们将开启定时器的方法

[NSTimer scheduledTimerWithTimeInterval:interval

                                                        target:timerTarget

                                                      selector:@selector(fire:)

                                                      userInfo:userInfo

                                                        repeats:repeats];

中的target换掉了,换成了 本类的对象,timer.在OneViewController中开启定时器的时候直接调用这个类方法,就不会造成循环引用.

原理看图

ios - NSTimer中target的self是强引用问题_第2张图片

废话不多说直接贴NSTimer 重写的方法

+ (NSTimer *) scheduledTimerWithTimeInterval:(NSTimeInterval)interval

                                      target:(id)aTarget

                                    selector:(SEL)aSelector

                                    userInfo:(id)userInfo

                                    repeats:(BOOL)repeats {

    TimerWeakTarget* timerTarget = [[TimerWeakTarget alloc] init];

    timerTarget.target = aTarget;

    timerTarget.selector = aSelector;

    timerTarget.timer = [NSTimer scheduledTimerWithTimeInterval:interval

                                                        target:timerTarget

                                                      selector:@selector(fire:)

                                                      userInfo:userInfo

                                                        repeats:repeats];

    return timerTarget.timer;

}

控制器dismiss以后可以正常被销毁.问题解决.喜欢的动动小手点下赞

你可能感兴趣的