iOS NSTimer循环引用的几种解决办法

发布时间 - 2026-01-11 00:52:54    点击率:

发生场景

在 Controller B 中有一个 NSTimer

@property (strong, nonatomic) NSTimer *timer;

你创建了它,并挂载到 main runloop

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 
target:self selector:@selector(timerAction:) userInfo:nil repeats:true];

然后退出 Controller B 的时候,忘记关掉 timer 了

Controller B 将不会释放,B 与 timer 循环引用。因为创建 timer 的时候把 self 直接写进去了。

方法一

既然不能直接传 self,那传 weakSelf 试试

__weak typeof(self) weakSelf = self;

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 
target:weakSelf selector:@selector(timerAction:) userInfo:nil repeats:true];

测试结果还是发生了循环引用,B 没有释放,timer 对 weakSelf 这个变量是强引用的,timer -> weakSelf -> B -> timer,三者之间形成循环引用。

方法二

设置一个包装类,包着 Controller B 放进 timer 中,像这样

我认为 Controller B 有几 MB 那么大,泄露了很浪费内存。

WeakWrap 只有几百个字节那么小,泄露了也没关系。

WeakWrap 中对 Controller B 弱引用,WeakWrap 包着 Controller B,传进 timer 中,就算忘记关 timer,也只是泄露了 WeakWrap 和 timer。

理论上还是有内存泄露,只不过比较少,如果一个 Controller 是频繁进出的,进出一次,丢失一个,如果有几十个泄露的 timer 挂在 main runloop 上会影响性能和流畅性,你想几十个 timer 一起 fire,又调用了 timer 事件响应方法,开销还是挺大的。

方法三

NSTimer 已知是会强引用参数 target:self 的了,如果忘记关 timer 的话,传什么进去都会被强引用。干脆实现一个 timer 算了,timer 的功能就是定时调某个方法,NSTimer 的调用时间是不精确的!它挂在 runloop 上受线程切换,上一个事件执行时间的影响。

利用 dispatch_asyn() 定时执行函数。看下面代码。

- (void)loop {
  [self doSomething];
  ......
  // 休息 time 秒,再调 loop,实现定时调用
  [NSThread sleepForTimeInterval:time];
  dispatch_async(self.runQueue, ^{
    [weakSelf loop];
  });  
}

dispatch_async 中调 loop 不会产生递归调用

dispatch_async 是在队列中添加一个任务,由 GCD 去回调 [weakSelf loop]

这办法解决了timer 不能释放,挂在 runloop 不能移除的问题。

利用这方法,我写了个不会发生循环引用的 timer,controller 释放,timer 也自动停止释放,甚至 timer 的 block 里面可以直接写 self,也不会循环引用。github下载地址

方法四

NSTimer 我之前没遇到过循环引用的问题,因为我一直都是配对使用,在 viewWillAppear 开启,在 viewWillDisappear 关闭,不关闭的话那么多 timer 挂载在 runloop 上感觉挺影响性能和流畅性的,就像管理内存一样,申请和释放配对使用,就不会泄露了,谁申请谁释放的原则。但是很大的团队的话,别人可能会写错,造成泄露,可以从技术上,团队编程规范上解决他。

比如定一些规范,Controller 退出一定要成功销毁,不能泄露内存。Block 里不能写 self 等等。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# ios  # nstimer  # 循环引用  # nstimer的循环引用  # IOS开发代码分享之用nstimer实现倒计时功能  # iOS中的NSTimer定时器的初步使用解析  # IOS 中NSTimer定时器的使用  # iOS NSTimer循环引用的办法  # iOS如何巧妙解决NSTimer的循环引用详解  # 挂在  # 递归  # 几十个  # 流畅性  # 包着  # 都是  # 是在  # 就像  # 去了  # 是有  # 也没  # 下载地址  # 那么多  # 中有  # 执行时间  # 你想  # 我一直  # 我认为  # 可以直接  # 写了 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 如何在阿里云ECS服务器部署织梦CMS网站?  如何破解联通资金短缺导致的基站建设难题?  Laravel如何实现API资源集合?(Resource Collection教程)  网站制作大概要多少钱一个,做一个平台网站大概多少钱?  香港服务器租用每月最低只需15元?  悟空识字如何进行跟读录音_悟空识字开启麦克风权限与录音  如何快速搭建个人网站并优化SEO?  javascript基于原型链的继承及call和apply函数用法分析  Laravel如何使用查询构建器?(Query Builder高级用法)  Laravel如何实现数据库事务?(DB Facade示例)  如何快速辨别茅台真假?关键步骤解析  Laravel怎么自定义错误页面_Laravel修改404和500页面模板  如何在七牛云存储上搭建网站并设置自定义域名?  百度输入法ai组件怎么删除 百度输入法ai组件移除工具  Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】  大同网页,大同瑞慈医院官网?  Windows Hello人脸识别突然无法使用  如何在万网开始建站?分步指南解析  Laravel怎么生成URL_Laravel路由命名与URL生成函数详解  Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】  Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程  如何快速完成中国万网建站详细流程?  Win11怎么设置默认图片查看器_Windows11照片应用关联设置  网站建设保证美观性,需要考虑的几点问题!  Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录  Laravel怎么使用artisan命令缓存配置和视图  Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】  详解jQuery中的事件  详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点  如何在 Python 中将列表项按字母顺序编号(a.、b.、c. …)  C++时间戳转换成日期时间的步骤和示例代码  Laravel如何从数据库删除数据_Laravel destroy和delete方法区别  Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程  JavaScript如何实现类型判断_typeof和instanceof有什么区别  Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践  如何注册花生壳免费域名并搭建个人网站?  Laravel如何部署到服务器_线上部署Laravel项目的完整流程与步骤  Laravel如何使用Gate和Policy进行权限控制_Laravel权限判定与策略规则配置  如何在IIS中新建站点并解决端口绑定冲突?  网站制作免费,什么网站能看正片电影?  PHP 500报错的快速解决方法  香港网站服务器数量如何影响SEO优化效果?  个人摄影网站制作流程,摄影爱好者都去什么网站?  武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?  Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区  如何使用 jQuery 正确渲染 Instagram 风格的标签列表  logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?  Laravel如何连接多个数据库_Laravel多数据库连接配置与切换教程  移动端脚本框架Hammer.js  Laravel如何发送邮件_Laravel Mailables构建与发送邮件的简明教程