iOS NSTimer 对Target的释放和repeat的关系及iOS9之后对内存回收机制的改变

在一次实践中,创建了控制器,控制器中创建了timer和手势,控制器的view被addsubview到window上,手动或者3s后从window上移除。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor=RGBA_COLOR(0, 0, 0, 0.4);
    _timer=[NSTimer timerWithTimeInterval:3.0
                                   target:self
                                 selector:@selector(action)
                                 userInfo:nil
                                  repeats:NO];
    UITapGestureRecognizer* tap= [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                        action:@selector(action)];
    [self.view addGestureRecognizer:tap];
}

并将timer加入到了当前的runloop中

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self startTimer];//开始计时
}

- (void)startTimer {
     [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}

-(void)action
{
    [_timer invalidate];
    _timer = nil;
    if (![self popNext]) {
        [self.view removeFromSuperview];
    }else {
        _timer=[NSTimer timerWithTimeInterval:3.0
                                       target:self
                                     selector:@selector(action)
                                     userInfo:nil
                                      repeats:NO];
        [self startTimer];
    }
    
}

运行在iOS9以下的系统手势触发会发上crash。

iOS NSTimer 对Target的释放和repeat的关系及iOS9之后对内存回收机制的改变_第1张图片
屏幕快照 2016-12-19 22.03.48.png

原因是该控制器创建后只有timer持有了该控制器。( timer对target对象强引用,当timer中repeat设置为NO时,timer到时间触发执行action后即对target不再引用;当timer中repeat设置为YES时,timer会持续持有target,直到手动invalidate timer后会释放。)当timer被invalidate后对象就释放了,再次访问timer就会报错。
可是ios9及以上的系统就不会,由于ios9以上系统对内存回收机制做了修改,当对象函数运行中对对象本身进行释放时,会到函数运行结束完后才真正释放,ios9以前的系统会在函数运行中立即释放对象,不会等到函数运行结束,如果该函数之后对对象进行操作就会crash。

你可能感兴趣的:(iOS NSTimer 对Target的释放和repeat的关系及iOS9之后对内存回收机制的改变)