前言

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. Python使用matplotlib可视化多个分组的密度图、使用seaborn中的kdeplot函数可视化多个分组的密度图(Density Plot)
  2. ASP.NET MVC Model元数据(五)
  3. 验证字符串是否以指定字符开头
  4. 离散图 java,Java实现离散Arnold变换(图像处理)
  5. 一个(伪)MaterialDesign风格的博客园皮肤
  6. 脉冲宽度调制pdm_STM32第七章-脉冲宽度调制
  7. ES6(三)——回调地狱和promise异步任务顺序执行(传参、错误处理)
  8. c#devexpres窗体划分 以及panelcontrol 相关操作
  9. Java 能抵挡住 JavaScript 的进攻吗?
  10. Centos6.9编译安装nginx1.14.0
  11. 基于Go的马蜂窝旅游网分布式IM系统技术实践
  12. linux 修改ftp默认端口号,在linux中添加ftp用户,以及修改FTP默认端口号
  13. centso系统命令来源查询
  14. 成功解决TypeError: only size-1 arrays can be converted to Python scalars
  15. 十天就能缓解颈椎病的保健操(图)
  16. ssd的smt_SMT 工厂基础参数要求
  17. 简单的视频剪辑入门制作软件,适合新手哦,快快收藏
  18. zlog日志系统开发中遇到的问题(2)
  19. MAC 合并多个jpg文件为PDF
  20. 传感网与物联网的区别,这二者之间的具体区别到底是什么?

热门文章

  1. tab栏圆点按钮_中间圆点怎么打-目录中小圆点横线上的点要一个个打上去?不用这么累!...
  2. php 随机数抽奖代码,php 抽奖概率 随机数 Web程序 - 贪吃蛇学院-专业IT技术平台...
  3. 雷鸟将邮件自动接收到新建的文件夹中
  4. arm linux 时钟源 信息,Linux时间子系统之一:clock source(时钟源)
  5. 哇...简洁的桌面时间屏保(30秒搞定)
  6. 英雄榜拍卖行服务器维护,魔兽世界手机英雄榜、远程拍卖行即将登陆国服
  7. 饥荒联机版服务器启动慢_饥荒联机版大型攻略——航海篇
  8. 【Cocos2d-x】Cocos2d-x精灵的性能优化
  9. SiTime MEMS硅晶振五大优势
  10. android逆向 registers,Android逆向——Android逆向进阶(3)