NSTimer使用场景

NSTimer来实现每隔一定时间执行制定的任务,例如最常见的广告轮播图,使用NSTimer实现这个功能很简单代码如下

    NSTimer *_timer;
_timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(timerEvent) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];

但是要记住只要触发了计时器这种操作在不用时一定要把计时器终止掉

[_timer invalidate];

一般我们终止这个操作都需要在这个界面销毁时。但是我们在初始化NSTimer时指定了触发事件为self,所以说selfNSTimer强引用了,而NSTimer对象又被加入了当前的循环中,所以说NSTimer被 Runloop 强引用了,所以导致self不会被释放掉就不会触发dealloc方法

实际上想这样操作

-(void)dealloc
{
[_timer invalidate];
}

但是由于self对象被持有,所有不会走dealloc,导致虽然已经退出当前界面了,但是计时器还是一致再执行,出现内存泄漏。


解决方法

思路很简单,初始化NSTimer时把触发事件的target替换成一个单独的对象,然后这个对象中NSTimerSEL方法触发时让这个方法在当前的视图self中实现。

利用RunTimetarget对象中动态的创建SEL方法,然后target对象关联当前的视图self,当target对象执行SEL方法时,取出关联对象self,然后让self执行该方法。

实现代码

@interface TableViewController ()
@property (nonatomic,strong) id timerTarget;
@end
static const void * TimerKey = @"TimerKey";
static const void * weakKey = @"weakKey";
@implementation TableViewController
- (void)viewDidLoad {
[super viewDidLoad];
_timerTarget = [NSObject new];
//初始化timerTarge对象 class_addMethod([_timerTarget class], @selector(timerEvent), (IMP)timMethod, "v@:");
//动态创建timerEvent方法 NSTimer *_timer;
_timer = [NSTimer timerWithTimeInterval:1 target:_timerTarget selector:@selector(timerEvent) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
//创建计时器target对象为_timerTarget objc_setAssociatedObject(_timerTarget, TimerKey, _timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(_timerTarget, weakKey, self, OBJC_ASSOCIATION_ASSIGN);
//将self对象与NSTimer对象与_timerTarget对象关联
}
void timMethod(id self,SEL _cmd)
{
TableViewController *tabview = objc_getAssociatedObject(self, weakKey);
[tabview performSelector:_cmd];
}
-(void)timerEvent
{
NSLog(@"%@",NSStringFromClass([self class]));
}
-(void)dealloc
{
NSTimer *timer = objc_getAssociatedObject(_timerTarget, TimerKey);
[timer invalidate];
NSLog(@"%@--dealloc",NSStringFromClass([self class]));
}

这样当视图销毁时因为当前视图不被任何对象所持有,所以会走dealloc方法,然后NSTimer执行invalidate也被销毁释放掉了。

说明

objc_setAssociatedObject(_timerTarget, weakKey, self,OBJC_ASSOCIATION_ASSIGN);

在把_timerTargetself关联时关联的属性一定要设置为OBJC_ASSOCIATION_ASSIGNOBJC_ASSOCIATION_ASSIGN为弱指针类型,如果设置为强制针,那么self_timerTarget就会发生相互强引用但是内存不能正确释放。

关于使用到的Runtime

 class_addMethod([_timerTarget class], @selector(timerEvent), (IMP)timMethod, "v@:");

动态的为类添加一个timerEventObjective-C方法,这个方法是由CtimMethod方法来实现的

void timMethod(id self,SEL _cmd)
{
TableViewController *tabview = objc_getAssociatedObject(self, weakKey);
[tabview performSelector:_cmd];
}

该方法是取到_timerTarget关联的对象,然后让该对象去执行timerEvent方法。

"v@:"是方法的参数,关于参数解释参考Objective-C type encodings

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

objc_getAssociatedObject(id object, const void *key)

这组方法是设置关联对象与获取关联对象key是关联对象的key

利用RunTime解决由NSTimer导致的内存泄漏的更多相关文章

  1. 使用HandyJSON导致的内存泄漏问题相关解决方法

    在移动开发中,与服务器打交道是不可避免的,从服务器拿到的接口数据最终都会被我们解析成模型,现在比较常见的数据传输格式是json格式,对json格式的解析可以使用原生的解析方式,也可以使用第三方的,我们 ...

  2. NSTimer、CADisplayLink 内存泄漏

    NSTimer.CADisplayLink 内存泄漏 内存泄漏的原因 CADisplayLink 要用 Taget 和 Selector 初始化,NSTimer 也可以用类似的方法初始化.这样初始化之 ...

  3. 使用block的时候,导致的内存泄漏

    明确,只要在block里边用到我们自己的东西,成员变量,self之类的,我们都需要将其拿出来,把它做成弱指针以便之后进行释放. 在ZPShareViewController这个控制器中,由如下代码: ...

  4. 精华阅读第 13 期 |常见的八种导致 APP 内存泄漏的问题

    本期是移动开发精英俱乐部的第13期文章,都是以技术为主,所以这里就不过多的进行赘述了,我们直接看干货内容吧!本文系ITOM管理平台OneAPM整理. 实际项目中的MVVM(积木)模式–序章 导读:开篇 ...

  5. 在Activity中使用Thread导致的内存泄漏

    https://github.com/bboyfeiyu/android-tech-frontier/tree/master/issue-7/%E5%9C%A8Activity%E4%B8%AD%E4 ...

  6. static关键字所导致的内存泄漏问题

    大家都知道内存泄漏和内存溢出是不一样的,内存泄漏所导致的越来越多的内存得不到回收的失手,最终就有可能导致内存溢出,下面说一下使用staitc属性所导致的内存泄漏的问题. 在dalvik虚拟机中,sta ...

  7. 一个驱动导致的内存泄漏问题的分析过程(meminfo->pmap->slabtop->alloc_calls)

    关键词:sqllite.meminfo.slabinfo.alloc_calls.nand.SUnreclaim等等. 下面记录一个由于驱动导致的内存泄漏问题分析过程. 首先介绍问题背景,在一款嵌入式 ...

  8. vue自定义指令导致的内存泄漏问题解决

    vue的自定义指令是一个比较容易引起内存泄漏的地方,原因就在于指令通常给元素绑定了事件,但是如果忘记了解绑,就会产生内存泄漏的问题. 看下面代码: directives: { scroll: { in ...

  9. 解决NSTimer存在的内存泄漏的问题

    创建定时器会在一定的间隔后执行某些操作,一般大家会这样创建定时器,这样创建的定时,self对定时器有个引用,定时器对self也有个引用,造成了循环引用,最终造成了内存泄漏,如果定时器在做下载的操作就会 ...

随机推荐

  1. HDU 1316 How Many Fibs?(java,简单题,大数)

    题目 /** * compareTo:根据该数值是小于.等于.或大于 val 返回 -1.0 或 1: public int compareTo(BigInteger val) 将此 BigInteg ...

  2. lightoj 1408 Batting Practice (概率问题,求期望,推公式)

    题意:一个人若连续进k1个球或连续不进k2个球,游戏结束,给出这个人不进球的概率p(注意:是不进球!!!),求到游戏结束时这个投球个数的期望. 不进球概率为p,进概率 q=1-p.设 f[i] 表示连 ...

  3. 有了 Docker,用 JavaScript 框架开发的 Web 站点也能很好地支持网络爬虫的内容抓取

    点这里 阅读目录 用 AngularJS(以及其它 JavaScript 框架)开发的 Web 站点不支持爬虫的抓取 解决方案 为什么公开我们的解决方案 实现 AngularJS 服务 结论   Pr ...

  4. ExtJs之 Ext.JSON

    <!DOCTYPE html> <html> <head> <title>ExtJs</title> <meta http-equiv ...

  5. nodejs的require模块及路径

    在nodejs中,模块大概可以分为核心模块和文件模块. 核心模块是被编译成二进制代码,引用的时候只需require表示符即可,如(require('net')). 文件模块,则是指js文件.json文 ...

  6. Spark源码分析环境搭建

    原创文章,转载请注明: 转载自http://www.cnblogs.com/tovin/p/3868718.html 本文主要分享一下如何构建Spark源码分析环境.以前主要使用eclipse来阅读源 ...

  7. Shell练习 统计单词个数,降序排列

    原文:https://leetcode.com/problems/word-frequency/ Write a bash script to calculate the frequency of e ...

  8. 540C: Ice Cave

    题目链接 题意: n*m的地图,'X'表示有裂痕的冰块,'.'表示完整的冰块,有裂痕的冰块再被踩一次就会碎掉,完整的冰块被踩一次会变成有裂痕的冰块, 现在告诉起点和终点,问从起点能否走到终点并且使终点 ...

  9. Android核心分析之十八Android电话系统之RIL-Java

    Android RIL-Java 123.jpg (2.09 KB, 下载次数: 1) 下载附件  保存到相册 2012-3-21 10:47 上传   RIL-Java在本质上就是一个RIL代理,起 ...

  10. 使用 JAX-RS 简化 REST 应用开发

    本文将详细介绍 Java EE 6 中所引入的对 JSR-311 (JAX-RS : Java API for RESTful Web Services) 的支持,并通过一个完整的示例应用程序展示 J ...