GCD Timer 和内存泄漏

更多内容请挪步我的博客

GCD 的 Timer 相对于 NSTimer 更加灵活、高效(无需在主线程和后台线程之间进行切换),而且 NSTimer 需要在合适的地方调用 invalid 以避免内存泄漏,所以平时比较常用。但是发现 GCD Timer 随便调用 API 的话也会造成内存泄漏。

GCD Timer 通过 Dispatch Source 实现,调用方法如:

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 
    2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^() {
    // do something
});
dispatch_resume(timer);

如果需要暂停定时器调用

dispatch_suspend(timer);

dispatch_suspend 和 dispatch_resume 应该是成对出现的。两者分别会减少和增加 dispatch 对象的挂起计数,但是没有 API 获取当前是挂起还是执行状态,所以需要自己记录。

dispatch_suspend 暂停队列并不意味着当前执行的 block 暂停

当暂停派发队列时需要注意,调用 dispatch_suspend 暂停一个队列,并不意味着暂停当前正在执行的 block,而是 block 可以执行完,但是接下来的 block 会被暂停,直到 dispatch_resume 被调用。

如果添加 dispatch_suspend 的调用后 timer 是无法被释放的。一般情况下会发生崩溃并报“EXC_BAD_INSTRUCTION”错误,看下 GCD 源码 dispatch source release 的时候判断了当前是否是在暂停状态。

dispatch_suspend 状态下无法释放

但是还有不一般的情况,如果暂停的代码加到 dispatch_source_set_event_handler 的 block 中,并不会发生崩溃,但是这个时候页面会无法释放造成内存泄漏!内存泄漏!内存泄漏!。

dispatch_source_set_event_handler(timer, ^() {
    // do something
    dispatch_suspend(timer);
});

Demo 代码点击这里,Demo 中进入“GCD Timer” 页面再退出,通过 Allocations 工具可以发现 GCDTimerViewController 没有释放,dealloc 无法执行。

怎么破?如果有需求按照上面这种方式写的话,添加个变量记录下 dispatch_suspend 是否被调用的状态,在 dealloc 时判断下,调用过 dispatch_suspend 则再调用下 dispatch_resume。或者暂停后不需要重新运行 timer 的话,取消 timer,一旦取消则不能再重新运行 timer,只能重建。

dispatch_source_cancel(timer);

参考内容

Mikeash GCD 介绍

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 程序中同步和异步是什么意思?有什么区别? 解释一:异步调用是通过使用单独的线程执行的。原始线程启动异步调用,异步调...
    风继续吹0阅读 4,635评论 1 2
  • 1. 并行和并发 简单来说,若说两个任务A和B并发执行,则表示任务A和任务B在同一时间段里被执行(更多的可能是二者...
    Z_Han阅读 3,805评论 0 8
  • Dispatch Sources 现代系统通常提供异步接口,允许应用向系统提交请求,然后在系统处理请求时应用可以继...
    YangPu阅读 2,750评论 0 0
  • 1. 什么是GCD? GCD 是 libdispatch 的市场名称,而 libdispatch 作为 Appl...
    弦暮阅读 3,938评论 0 2
  • 写到小说里。
    edf6474f7029阅读 1,144评论 0 0