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

最新文章

  1. WebViewJavascriptBridge的暂时理解
  2. C#通用类型转换 Convert.ChangeType
  3. makefile常用函数
  4. Algorithm: cartesian tree
  5. 【英语】Bingo口语笔记(75) - 元音辅音的辨读
  6. 3123 高精度练习之超大整数乘法 - Wikioi
  7. CentOS 安装apache 及所需的 apr,apr-util,pcre
  8. c#之函数创建和闭包
  9. HDU 1312 Red and Black(DFS,板子题,详解,零基础教你代码实现DFS)
  10. CentOS7中PPTP的配置
  11. Django积木块七——视频
  12. 【UOJ347】【WC2018】通道 边分治 虚树 DP
  13. atoi 和 itoa的实现
  14. OOP 面向对象的理解
  15. Redis后台监控与管理CacheCloud
  16. bat遍历目录
  17. jdk settings
  18. Nginx的日志优化
  19. Git——新手入门与上传项目到远程仓库GitHub(转)
  20. Unity3D游戏开发最佳实践20技巧(三)

热门文章

  1. Hello world,Hello 2015,Bye 2014
  2. IT架构之IT架构模型——思维导图
  3. 压测2.0:云压测 + APM = 端到端压测解决方案
  4. javascript中的JSON序列化与反序列化
  5. HTML5程序设计--SVG
  6. 【mysql5.6】连接vs2010
  7. Codeforces Round #336 (Div. 2)C. Chain Reaction DP
  8. 如何使用JMeter来实现更大批量的并发的解决方案(即如何设置controller和Agent)
  9. linux下如何查看主机的外网ip地址
  10. ffmpeg转码MPEG2-TS的音视频同步机制分析