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

发生场景

在 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 等等。

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

(0)

相关推荐

  • IOS 中NSTimer定时器的使用

    IOS 中NSTimer定时器的使用 NSTimery 定时器,主要用于进行定时执行指定方法,常用场景如:获取验证码的按钮倒计时:图片轮播定时. 1 使用注意事项: 1.1 倒计时时间间隔(时间单位是秒) 1.2 指定的执行方法 1.3 实现指定执行方法的对象 1.4 是否重复执行 2 对象的内存管理及销毁 2.1 使用方法" invalidate "进行停止 2.2 将对象设置为" nil " 2.3 特别是在返回到其他视图控制器的时候,要在方法" -

  • iOS中的NSTimer定时器的初步使用解析

    创建一个定时器(NSTimer) - (void)viewDidLoad { [super viewDidLoad]; [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(actionTimer:) userInfo:nil repeats:YES]; } - (void)actionTimer:(NSTimer *)timer { } NSTimer默认运行在default mode下,default

  • IOS开发代码分享之用nstimer实现倒计时功能

    用nstimer实现倒计时功能,废话不多说,直接上代码,详细解释请参照注释 // [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES];   // - (void)timerFireMethod:(NSTimer *)theTimer {     BOOL timeStart = YES;     NSCalend

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

    发生场景 在 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 的

  • iOS NSTimer循环引用的办法

    在当前控制器(ViewController)的view上添加了一个自定义的view(LXFTimerView), LXFTimerView在成功创建出来后添加了定时器NSTimer并加入RunLoop开始工作, 当在当前控制器里将LXFTimerView移除掉后,定时器还在工作,而且LXFTimerView里的dealloc并没有调用 代码 LXFTimerView.m #import "LXFTimerView.h" @interface LXFTimerView() /** 定时器

  • iOS block循环引用详解及常见误区

    Block循环引用 什么情况下block会造成循环引用 ARC 情况下 block为了保证代码块内部对象不被提前释放,会对block中的对象进行强引用,就相当于持有了其中的对象,而如果此时block中的对象又持有了该block,就会造成循环引用. 常见误区 误区一.所有block都会造成循环引用 在block中,并不是所有的block都会循造成环引用,比如UIView动画block.Masonry添加约束block.AFN网络请求回调block等.     1. UIView动画block不会造

  • golang包循环引用的几种解决方案总结

    目录 1. golang 包循环引用的几种解决方案 1.1. 前言 1.2. 新建公共接口包(父包), 将需要循环调用的函数或方法抽象为接口 1.3. 新建公共组合包(子包), 在组合包中组合调用 1.4. 全局存储需要相互依赖的函数, 通过关键字进行调用 1.5. 不需要回调结果的可以通过事件总线 (eventBus) 解耦 总结 1. golang 包循环引用的几种解决方案 1.1. 前言 golang 为了加速编译, 不允许包循环引用.通常来说, 只要你的包规划得好, 严格规范单向调用链

  • 详解spring中aop不生效的几种解决办法

    先看下这个问题的背景:假设有一个spring应用,开发人员希望自定义一个注解@Log,可以加到指定的方法上,实现自动记录日志(入参.出参.响应耗时这些) package com.cnblogs.yjmyzz.springbootdemo.aspect; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy

  • Oracle表空间不足的两种解决办法

    首先查询表空间的大小以及文件路径地址 select tablespace_name, file_id, file_name,round(bytes/(1024*1024),0) total_space from dba_data_files order by tablespace_name; 解决方案一 //修改表空间大小(32000可改为想要的数值) alter database datafile '要修改的数据文件地址' resize 32000m; 解决方案二 //新增数据文件 ALTER

  • iOS下border-image不起作用的解决办法

    上周五突然接到现场的一个需求,做一个移动端的劳模展示页面.现场美工把原型图发了过来.这个样子的: 说实在的很想吐槽一下我们美工的审美哈,不过这不是重点. 因为边框是需要特殊花纹的所以打算用border-image实现. .example{ -- border:6px solid transparent; -webkit-border-image:url(../img/border_img.jpg) 6 6 round; border-image:url(../img/border_img.jpg

  • Ajax打开新窗口被浏览器拦截的两种解决办法

    最近在做支付时发现打开支付窗口时被浏览器拦截了,百度了一下才发现是因为打开窗口前用ajax验证是否能支付,所以不是用户主动触发的打开ixin窗口,浏览器认为这样不安全,所以给拦截了. 解决办法一 先开始打开一个空的新窗口,然后改变新窗口的url,具体代码为 var wd = window.open(); $.ajax({ type: "POST", dataType: "json", url: URL, data: {orderNo:orderNo}, succes

  • Android Button 自带阴影效果另一种解决办法

    在Android 5.0以后的版本中,定义一个button时,系统自动会加一个阴影的效果,有的时候这种效果看起来比较好,有的时候不符合UI的设计要求,这时候就需要手动去掉阴影. 网上很多文章写了解决办法,就是给button加一句话style="?android:attr/borderlessButtonStyle",这个确实能解决问题,但是又带来了另外一个问题,就是一般情况下,在写布局的时候,都会给每个控件写一个style,这样方便复用,比如我写了一个button,引了一个style,

  • iOS UIButton 点击无响应的解决办法

    前言 在日常开发中我们经常和按钮打交道,但是有时候会碰到比较难处理的问题,就是按钮不响应点击事件,这时候我们需要从下面几方面排查 按钮不响应的原因 1.按钮添加到了一个没有开启用户交互的父View上,例如UIImageView,这时候开启父试图的交互 view.userInteractionEnabled = YES 设为YES就可以解决问题 2.按钮自身被遮挡,点击的时候根本就没有点击到button,而是他上面一层View,自然就不会响应 这里有个看图层的方法,下图点击那个红框的按钮就可以看到

随机推荐