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. Effeckt.css项目:CSS交互动画应用集锦

    目前,网上有大量基于CSS转换的实验和示例,但它们都过于分散,而Effeckt.css的目标就是把所有基于CSS/jQuery动画的应用集中起来,例如:弹窗.按钮.导航.列表.页面切换等等. Effe ...

  2. MVC 中 Razor 无限分类的展示

    在MVC的Razor视图展示无级分类的办法,在网上看了很多资料,大多搞得很高大上.可能本人水平有限,实在是不会用. 那我就用最简单爆力的办法来做. Model: public class NewsCa ...

  3. POJ 2013

    #include <iostream> #include <string> #define MAXN 20 using namespace std; string _m[MAX ...

  4. UVA 11401 - Triangle CountingTriangle Counting 数学

    You are given n rods of length 1,2, . . . , n. You have to pick any 3 of them and build a triangle. ...

  5. CSS中的长度值

    以下总结来自慕课网(依然比较浅显). 长度单位总结一下,目前比较常用到px(像素).em.% 百分比,要注意其实这三种单位都是相对单位. 1.像素 像素为什么是相对单位呢?因为像素指的是显示器上的小点 ...

  6. C/C++ 位域知识小结

    C/C++ 位域知识小结 几篇较全面的位域相关的文章: http://www.uplook.cn/blog/9/93362/ C/C++位域(Bit-fields)之我见 C中的位域与大小端问题 内存 ...

  7. lines---hdu5124(离散化+数组模拟)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5124 就是有n条在x轴的线段,给你线段的左右端点,每条线段都会覆盖点,求出最多被覆盖多少次: #inc ...

  8. iOS Core Telephony Framework

    Core Telephony Framework(核心通讯框架) 概述: 这个库的前缀为CT(Core Telephony),主要用来获得用户通讯相关信息,我们可以使用这些信息来定义外部接口以便自己使 ...

  9. 314. Binary Tree Vertical Order Traversal

    题目: Given a binary tree, return the vertical order traversal of its nodes' values. (ie, from top to ...

  10. Java:进制转换

    进制转换是常常需要的一种数据处理,在java中的一些类中封装了具有转换功能的方法,这个不做介绍.其实,进制之间的转化是通过先位异或&,再位移动>>>的方式实现的. 例如,对于 ...