前言

iOS 的开发过程中定时任务中能找到使用的场景,然而在 iOS 中默认的有关 timerapi 总是那么晦涩难用,而且暗坑不断,一旦遇上,会让你一脸懵逼,为了不再同一个地方跌倒两次,我决心花些时间做一篇总结,也用以提醒读者,谨慎使用。      之前在做一个空白页的计时器的时候使用到了 CADisplayLink,这货把我坑惨了, 循环引用导致内存随着时间的增加而上升,短时间使用没啥感觉,要不是使用工具这是很难发现的。

分析

通常,在解决循环引用的时候我们会引入 weak , 通过 weak 修饰打破循环引用中的 , 如:

    @property (nonatomic, weak) CADisplayLink *link;self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(fireAction)];[self.link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
复制代码

然而,这样做 link 直接不工作了, 因为 link 没有别的地方引用,当它初始化完成立即就被释放掉了 。那么换一种思路呢?

    __weak typeof(self) weakSelf = self;self.link = [CADisplayLink displayLinkWithTarget:weakSelf selector:@selector(fireAction)];[self.link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
复制代码

这样做也是徒劳的,当 self.link 持有 weakSelf 时也就是持有了 self, 而 link 是通过 target 强持有的 self 所以还是无法打破形成的环,我们通过 Memory Graph 就可以检测是否内存图关系:

这是 RunLoopTimer 的内存关系,再看看 Timertarget 的关系:

方案

既然这个环用常规的方法无法打破,那该怎么办呢? 这时候 NSProxy 就可也发挥它的长处了。我们实现一个 NSProxy 的子类 WeakProxyWeakProxy 弱引用一个 target ,然后在通过 WeakProxy 消息转发到 target 从而达到破除循环的效果:

- (id)forwardingTargetForSelector:(SEL)selector {return _target;
}- (void)forwardInvocation:(NSInvocation *)invocation {void *null = NULL;[invocation setReturnValue:&null];
}- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
复制代码

proxy 弱引用的 target 所以不影响 target 的正常释放,当 target 释放后,link 引用计数减一 link 释放,proxy 引用计数减一也会释放,因此,原来的环不在了,完美解决了相互引用的问题。

源码 Demo

iOS 中的 timer 任务(寻找内存恶鬼之旅)相关推荐

  1. iOS中滤镜处理及相关内存泄漏问题的解决

    iOS中滤镜处理及相关内存泄漏问题的解决 参考文章: (1)iOS中滤镜处理及相关内存泄漏问题的解决 (2)https://www.cnblogs.com/ToBeTheOne/p/5708906.h ...

  2. iOS中内存管理的问题——堆和栈

    计算机系统中,运行的应用程序的数据都是保存在内存中的,不同类型的数据,保存的内存区域不同: 1)栈区(stack)由编译器自动分配并释放,一般保存函数的参数值.局部变量 2)堆区(heap)由程序员分 ...

  3. (转载)深入了解iOS中的OOM(低内存崩溃)

    英文原文:https://programmer.ink/think/learn-more-about-oom-low-memory-crash-in-ios.html 中文翻译:https://www ...

  4. 深入了解iOS中的OOM(低内存崩溃)

    在iOS开发过程或者用户反馈中,可能会经常看到这样的情况,用着用着就崩溃了,而在后台查看崩溃栈的时候,找不到崩溃日志.其实这大多数的可能是系统产生了低内存崩溃,也就是OOM(还有一种可能是主线程卡死, ...

  5. iOS中引用计数内存管理机制分析

    在 iOS 中引用计数是内存的管理方式,虽然在 iOS5 版本中,已经支持了自动引用计数管理模式,但理解它的运行方式有助于我们了解程序的运行原理,有助于 debug 程序. 操作系统的内存管理分成堆和 ...

  6. 【iOS系列】-iOS中内存管理

    iOS中创建对象的步骤: 1,分配内存空间,存储对象 2,初始化成员变量 3,返回对象的指针地址 第一:非ARC机制: 1,对象在创建完成的同时,内部会自动创建一个引用计数器,是系统用来判断是否回收对 ...

  7. iOS中关于NSTimer使用知多少

    看到这个标题,你可能会想NSTimer不就是计时器吗,谁不会用,不就是一个能够定时的完成任务的东西吗? 我想说你知道NSTimer会retain你添加调用方法的对象吗?你知道NSTimer是要加到ru ...

  8. ios 中的小技巧 - 总有你想要的 一

    UITableView的Group样式下顶部空白处理 在viewWillAppear里面添加如下代码: //分组列表头部空白处理 CGRect frame = myTableView.tableHea ...

  9. 内存恶鬼drawRect

    转载于:http://bihongbo.com/       内存恶鬼drawRect 标题有点吓人,但是对于drawRect的评价倒是一点都不过分.在平日的开发中,随意覆盖drawRect方法,稍有 ...

最新文章

  1. 2022中国人工智能芯片行业研究报告
  2. 《天天数学》连载43:二月十二日
  3. 事件相互独立的几种不同说法
  4. Java final关键字详解
  5. 小美赛(认证杯)ABCD题翻译
  6. 远程桌面 无法打开连接文件default.rdp
  7. MySQL卸载不干净-MySQL Connector Net xxx文件怎么也删除不了?一招解决它
  8. wordpress入门主题_WordPress播客入门指南
  9. 臭氧9母带处理工具:iZotope Ozone 9 Advanced for Mac
  10. stm32f103读取红外接收HS0038A2
  11. vm连接服务器桌面,Vmware之使用Windows自带的远程桌面连接
  12. 人员属性识别 PETA数据集下载
  13. 后端开发工程师不懂这些就危险了
  14. win32-c语言实现俄罗斯方块
  15. 基于Redis的incr、发布订阅防并发和setnx、轮询防并发的理解
  16. LQ0270 手机尾数【水题】
  17. 实现html页面的倒计时
  18. Java实现微信小程序支付(支付,提现,退款)
  19. 扒一扒DDR4的新功能和PCB设计上的一些注意事项--转
  20. wannafly summer camp day6

热门文章

  1. 大专找不到计算机工作吗,大专毕业就没工作?
  2. AWS的“炮仗”与Serverless
  3. python爬取的图片无法显示的问题
  4. S2-045 Struts 2漏洞(CVE-2017-5638) 实战
  5. 如何转潜在客户为客户?
  6. 怎么样挑选关键才才能让网站优化效果超凡出众
  7. 5月12日 疯狂猜成语反省总结站立会议-----赵静娜,尹亚男,胡潇丹,郭林林,袁雪,杨霏...
  8. python 写数值模拟器_python实现模拟器爬取抖音评论数据的示例代码
  9. python request url编码_Python爬虫进阶——Request对象之Get请求与URL编码【英雄联盟吧】...
  10. ERTMS架构及UIC对高速铁路的描述