iOS 中的 timer 任务(寻找内存恶鬼之旅)
前言
在 iOS
的开发过程中定时任务中能找到使用的场景,然而在 iOS
中默认的有关 timer
的 api
总是那么晦涩难用,而且暗坑不断,一旦遇上,会让你一脸懵逼,为了不再同一个地方跌倒两次,我决心花些时间做一篇总结,也用以提醒读者,谨慎使用。 之前在做一个空白页的计时器的时候使用到了 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
就可以检测是否内存图关系:
这是 RunLoop
与 Timer
的内存关系,再看看 Timer
与 target
的关系:
方案
既然这个环用常规的方法无法打破,那该怎么办呢? 这时候 NSProxy
就可也发挥它的长处了。我们实现一个 NSProxy
的子类 WeakProxy
,WeakProxy
弱引用一个 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 任务(寻找内存恶鬼之旅)相关推荐
- iOS中滤镜处理及相关内存泄漏问题的解决
iOS中滤镜处理及相关内存泄漏问题的解决 参考文章: (1)iOS中滤镜处理及相关内存泄漏问题的解决 (2)https://www.cnblogs.com/ToBeTheOne/p/5708906.h ...
- iOS中内存管理的问题——堆和栈
计算机系统中,运行的应用程序的数据都是保存在内存中的,不同类型的数据,保存的内存区域不同: 1)栈区(stack)由编译器自动分配并释放,一般保存函数的参数值.局部变量 2)堆区(heap)由程序员分 ...
- (转载)深入了解iOS中的OOM(低内存崩溃)
英文原文:https://programmer.ink/think/learn-more-about-oom-low-memory-crash-in-ios.html 中文翻译:https://www ...
- 深入了解iOS中的OOM(低内存崩溃)
在iOS开发过程或者用户反馈中,可能会经常看到这样的情况,用着用着就崩溃了,而在后台查看崩溃栈的时候,找不到崩溃日志.其实这大多数的可能是系统产生了低内存崩溃,也就是OOM(还有一种可能是主线程卡死, ...
- iOS中引用计数内存管理机制分析
在 iOS 中引用计数是内存的管理方式,虽然在 iOS5 版本中,已经支持了自动引用计数管理模式,但理解它的运行方式有助于我们了解程序的运行原理,有助于 debug 程序. 操作系统的内存管理分成堆和 ...
- 【iOS系列】-iOS中内存管理
iOS中创建对象的步骤: 1,分配内存空间,存储对象 2,初始化成员变量 3,返回对象的指针地址 第一:非ARC机制: 1,对象在创建完成的同时,内部会自动创建一个引用计数器,是系统用来判断是否回收对 ...
- iOS中关于NSTimer使用知多少
看到这个标题,你可能会想NSTimer不就是计时器吗,谁不会用,不就是一个能够定时的完成任务的东西吗? 我想说你知道NSTimer会retain你添加调用方法的对象吗?你知道NSTimer是要加到ru ...
- ios 中的小技巧 - 总有你想要的 一
UITableView的Group样式下顶部空白处理 在viewWillAppear里面添加如下代码: //分组列表头部空白处理 CGRect frame = myTableView.tableHea ...
- 内存恶鬼drawRect
转载于:http://bihongbo.com/ 内存恶鬼drawRect 标题有点吓人,但是对于drawRect的评价倒是一点都不过分.在平日的开发中,随意覆盖drawRect方法,稍有 ...
最新文章
- Python使用matplotlib可视化多个分组的密度图、使用seaborn中的kdeplot函数可视化多个分组的密度图(Density Plot)
- ASP.NET MVC Model元数据(五)
- 验证字符串是否以指定字符开头
- 离散图 java,Java实现离散Arnold变换(图像处理)
- 一个(伪)MaterialDesign风格的博客园皮肤
- 脉冲宽度调制pdm_STM32第七章-脉冲宽度调制
- ES6(三)——回调地狱和promise异步任务顺序执行(传参、错误处理)
- c#devexpres窗体划分 以及panelcontrol 相关操作
- Java 能抵挡住 JavaScript 的进攻吗?
- Centos6.9编译安装nginx1.14.0
- 基于Go的马蜂窝旅游网分布式IM系统技术实践
- linux 修改ftp默认端口号,在linux中添加ftp用户,以及修改FTP默认端口号
- centso系统命令来源查询
- 成功解决TypeError: only size-1 arrays can be converted to Python scalars
- 十天就能缓解颈椎病的保健操(图)
- ssd的smt_SMT 工厂基础参数要求
- 简单的视频剪辑入门制作软件,适合新手哦,快快收藏
- zlog日志系统开发中遇到的问题(2)
- MAC 合并多个jpg文件为PDF
- 传感网与物联网的区别,这二者之间的具体区别到底是什么?
热门文章
- tab栏圆点按钮_中间圆点怎么打-目录中小圆点横线上的点要一个个打上去?不用这么累!...
- php 随机数抽奖代码,php 抽奖概率 随机数 Web程序 - 贪吃蛇学院-专业IT技术平台...
- 雷鸟将邮件自动接收到新建的文件夹中
- arm linux 时钟源 信息,Linux时间子系统之一:clock source(时钟源)
- 哇...简洁的桌面时间屏保(30秒搞定)
- 英雄榜拍卖行服务器维护,魔兽世界手机英雄榜、远程拍卖行即将登陆国服
- 饥荒联机版服务器启动慢_饥荒联机版大型攻略——航海篇
- 【Cocos2d-x】Cocos2d-x精灵的性能优化
- SiTime MEMS硅晶振五大优势
- android逆向 registers,Android逆向——Android逆向进阶(3)