iOS-NSTimer

180 阅读2分钟

「这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战

在项目开发过程中无可避免的会使用到计时器的功能, 尤其是在电商和金融项目中居多, iOS中使用计时器,系统提供了 NSTimer 的API来实现.

NSTimer 计时器的功能是, 在指定间隔时间向一个对象发送一条消息, 我们可以配合系统通知来达到广播的效果.

通过用于 定时更新界面 定时刷新请求等等场景, 但是在使用 NSTimer 的过程中, 需要注意的地方很多, 比如强弱引用 内存释放 等问题.

我们针对这些问题来进行了解一下, 避免项目中出现类的问题.

  1. 内存泄漏
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(update) userInfo:nil repeats:YES];

这段代码有一个问题就是, 导致timer无法释放.

创建一个无限循环的timer, 投入到当前线程的Runloop中, 这个时候Runloop会引用timer, 而timer会引用self, 但是self又持有timer, 这样的情况timer无法释放, 从而导致内存泄漏.

要破解这种情况也很简单, 苹果已经给出了解决的方案, 就是需要显式的调用 [timer invalidate], 这样就可以将 timer 释放掉了, 为了安全起见 self.timer = nil; 如此操作 timer 肯定已经释放的干干净净了.

但是调用的时机也是有讲究的, 在我接手的代码里面, 我见到过在 dealloc 里面的调用的, = =
这样的调用肯定是错误的, timer 会引用self, 那么在 timer 释放之前, self是不会释放的, 也就是说不会调用 dealloc 的, 所以这样是错误的.

可以在 viewDidDisappear: 的方法中调用就可以了.

- (void)viewWillAppear:(BOOL)animated {  
    [super viewWillAppear:animated];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(update) userInfo:nil repeats:YES];
}

- (void)viewDidDisappear:(BOOL)animated {
  [super viewDidDisappear:animated];
  [self.timer invalidate];
}

还有一个需要注意的地方就是在调用 invalidate 方法之后, 如果 timer 是self最后一个持有target的对象, 那么self也会被销毁掉了, 那么在 invalidate 之后调用 self 的话, 就会造成野指针的错误.
比如如下操作:

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.timer invalidate]; 
    [self.tableView reloadData];
}

self 在调用 invalidate 之后已经销毁了, 那么调用 [self.tableView reloadData]; 就会报野指针错误了.

当然计时器的功能也不仅仅只能通过 NSTimer 来实现, 也可以通过 dispatch timer 的方式来实现, 这里就先不展开讲解了, 毕竟这是一篇将 NSTimer 的文章, 后续会有对 dispatch timer 的文章, 谢谢大佬点评.