object-c定时器

object-c定时器会自己主动retain当前的使用者,假设不注意调用invalidate,则非常easy引起循环引用导致内存泄露。以下的思路提供了一套还算可行的解决方式。

举例:

常常在viewController中有可能有自己主动刷新界面的需求。 获取数据失败后。每隔10秒自己主动刷新又一次获取数据,这个时候使用NSTimer是一个非常方便的事情。普通情况下直接创建一个NSTimer的repeat对象,然后实现相应的timerFireMethod方法。 当用户主动点击返回button时候,此界面应该被释放。可是因为NSTimer retain了当前的viewController,导致界面内存泄露。 你可能会说在dealloc中调用invalidate,可是必须明确dealloc根本就不会调用,当然viewDidDisappear也一样不会被调用。

前一段时间看了effective object-c,学习了一种非常好的思想,现分享出来。

给NSTimer加入一个类别,使用block的方式传递timerFireMethod。代码例如以下:

@implementation NSTimer(LPBLocks)

+(NSTimer*) lpScheduleTimerWithTimerInternal:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(lpTimerBlockInvoke:) userInfo:[block copy] repeats:repeats];
}
+(void)lpTimerBlockInvoke:(NSTimer*)timer
{
void(^block)() = timer.userInfo; if(block){
block();
}
}
@end

这个scheduledTimer方法也会retain target,可是因为这是一个类方法。它保留的是类对象,因此也就不会有什么问题。

它传入要运行的block, 然后在回调函数中通过userInfo得到block,并运行。

改进:

这个已经是一个非常大的改进了。我们能够在代码中放心的传入block代码。只是细致思考一下。假设在block中引入了viewController的成员,并且timer又作为成员变量存在于viewController中。

比如例如以下的代码:

@interface LPNextViewController ()
{
NSTimer* refreshTimer;
}

这样viewController和refreshTimer又陷入了循环引用的逻辑圈里。当然能够在block中使用weak_self的方式避免循环引用,可是写起代码来总是有些不顺手。并且还必需要外部使用者显式的进行。

于是非常easy想到。应该封装到一个专门的LPTimer类中。它负责持有NSTimer。同一时候NSTimer的block使用LPTimer的weak版本号。

@interface LPTimer ()
{
NSTimer* _pollTimer; //timer selector
__weak id _weak_target;
SEL _selector;
id _userInfo;
}
@end
-(void)scheduleTimerWithTimerInternal:(NSTimeInterval)interval
target:(id)target
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats
{
__weak id weak_self = self; _weak_target = target;
_selector = aSelector;
_userInfo = userInfo; //借用第一个版本号的block思想
//使用了第二层间接,调用_weak_target的aSelector方法。
//这样能够把stopTimer给封装进去。外部不须要管理timer的stop。
_pollTimer = [NSTimer lpScheduleTimerWithTimerInternal:1 block:^{
[weak_self doTimer];
} repeats:repeats];
}

上面的代码LPTimer持有NSTimer对象。而NSTimer运行的block使用的是weak_self。

它在timer触发的时候调用自身的doTimer方法。在doTimer中负责将方法传递给外部的使用者。

-(void)doTimer
{
if ([_weak_target respondsToSelector:_selector]) {
[_weak_target performSelector:_selector withObject:self];
}
else{
DLog(@"WARNNING: unknown selector");
}
}

_weak_target是外部的使用者。 外部的使用者能够将LPTimer看成是一个普通的对象即可,持有它也不会有什么问题。 LPTimer保留一个弱引用指向外部的使用者。在时间到timer触发的时候,会先调到NStimer的block中。然后传递到LPTimer的doTimer中。然后调用到_weak_target的selector中。

必须注意释放NStimer对象,在LPTimer释放的时候调用NSTimer的invalidate方法。

-(void)stopTimer
{
DLog(@"");
[_pollTimer invalidate];
}
-(void)dealloc
{
[self stopTimer];
DLog(@"");
}

事实上。使用者都是使用的LPTimer类,那么应该让LPTimer表现的和NSTimer的行为一模一样, 使用组合方式的适配器模式就能够轻松搞定。

总结:

主要的思想就是NSTimer会retain一个对象,如今让它retain类对象。

当时候到来进行触发的时候,由NSTimer类对象触发到Block中。继而触发到外部的LPTimer普通对象中。

在普通对象中我们就能够自由的进行处理了。使用weak_target使LPTimer弱引用外部使用者,断开外部使用者与LPTimer的关联。

使用weak_self断开LPTimer与NStimer的循环关联。 个人觉得还算不错的思想, 有须要的欢迎讨论。

object-c编程tips-timer的更多相关文章

  1. Java并发编程:Timer和TimerTask(转载)

    Java并发编程:Timer和TimerTask(转载) 下面内容转载自: http://blog.csdn.net/xieyuooo/article/details/8607220 其实就Timer ...

  2. Java并发编程:Timer和TimerTask

    Java并发编程:Timer和TimerTask 下面内容转载自: http://blog.csdn.net/xieyuooo/article/details/8607220 其实就Timer来讲就是 ...

  3. 16、Java并发编程:Timer和TimerTask

    Java并发编程:Timer和TimerTask(转载) 下面内容转载自: http://blog.csdn.net/xieyuooo/article/details/8607220 其实就Timer ...

  4. Java编程Tips

    原文: Java编程中"为了性能"尽量要做到的一些地方 作者: javatgo 最近的机器内存又爆满了,除了新增机器内存外,还应该好好review一下我们的代码,有很多代码编写过于 ...

  5. [C++]高效C/C ++编程tips

    Effective C++ 视C++ 为一个语言联邦(C.Object-Oriented C++.Template C++.STL) 宁可以编译器替换预处理器(尽量以const.enum.inline ...

  6. 编程Tips集锦

    以下是自己编程的一些小贴士,记录,总结提高自己. 1.python中集合类型的查找,尽量用dict or set类型. dict和set类型,在python内部的实现都是使用hash映射,查找的时间复 ...

  7. 前端编程tips

    1.ts less 网上搜视频教程,不用太复杂的,短短几分钟视频基本就对其入门了,比自己搜官网学习更方便. 常用的ts技术:let name:string="";  let obj ...

  8. 编程Tips

    三元运算符 Vb中的iif(expr,truepart,falsepart)和C#中的expr?truepart:falsepart. 无论expr的结果是true还是false,true/false ...

  9. stm8编程tips(stvd)

    编译完成时显示程序占用的flash和ram大小 将附件压缩包中的mapinfo.exe解压到stvd的安装路径\stvd中 在工程上点右键选settings 右侧的选项卡选择Linker,将categ ...

  10. C#中Timer使用及解决重入问题

    C#中Timer使用及解决重入问题 ★介绍 首先简单介绍一下timer,这里所说的timer是指的System.Timers.timer,顾名思义,就是可以在指定的间隔是引发事件.官方介绍在这里,摘抄 ...

随机推荐

  1. 关于android中postDelayed方法的讲解

    这是一种可以创建多线程消息的函数使用方法:1,首先创建一个Handler对象 Handler handler=new Handler(); 2,然后创建一个Runnable对象Runnable run ...

  2. iOS开发RunTime之函数调用

    文章来自小笨狼的iOS博客,一直认为csdn的博客UI不太好看,看博客不太爽.所以自己搭建了一个博客. 欢迎各位去链接中看我的博客.也欢迎大家加QQ群讨论iOS技术问题 经过两个多月的面试,工作最终尘 ...

  3. 圣魔大战3(Castle Fantisia)艾伦希亚战记完美攻略

    作为城堡幻想曲系列续作,艾伦希亚战记继承了前作的战棋+养成模式进行游戏. (城堡幻想曲3,纠正大家个错误哦,不是圣魔大战3,圣魔大战是城堡幻想曲2,圣魔大战不是个系列,艾伦西亚战记==艾伦希亚战记,一 ...

  4. 基于visual Studio2013解决面试题之1101差值最小

     题目

  5. QT对话框中show和exec的区别

    转自:http://hi.baidu.com/wangjuns8/blog/item/24b382460dd1c1338694737d.html QDialog的显示有两个函数show()和exec( ...

  6. 【Demo 0009】Android 组件(BroadcastReceiver)

    本章学习要点:        1.  了解Broadcast的作用;        2.  掌握自定义广播和系统广播的接收:        3.  掌握广播的发送:

  7. tar打包过滤某个文件及文件夹

    ip=ip add|grep eth0|grep -i inet|awk '{print $2}'|cut -d '/' -f 1 cd /data tar -zvcf `echo $ip`_`dat ...

  8. Swift - 常用文件目录路径获取(Home目录,文档目录,缓存目录等)

    当前位置: 首页 > 编程社区 > Swift > Swift - 常用文件目录路径获取(Home目录,文档目录,缓存目录等) Swift - 常用文件目录路径获取(Home目录,文 ...

  9. PreTranslateMessage和TranslateMessage区别

    PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,绝大多数本窗口的消息都要通过这里,比较常用,当需要在MFC之前处理某些消息时,常常要在这里添加代码. ...

  10. WindowsPhone8中实现圆形图片的生成显示

    原文 WindowsPhone8中实现圆形图片的生成显示 很多软件中(比如QQ)用到了许多圆形图片,作为用户头像等等,原始图片往往是方形的,那么怎么样将方形的图片显示成圆形呢? 一种方法是当背景为固定 ...