您的位置:首页 > 其它

利用RunTime解决由NSTimer导致的内存泄漏

2015-12-09 19:08 211 查看

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
,所以说
self
NSTimer
强引用了,而
NSTimer
对象又被加入了当前的循环中,所以说
NSTimer
被 Runloop 强引用了,所以导致
self
不会被释放掉就不会触发
dealloc
方法

实际上想这样操作

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


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

解决方法

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

利用
RunTime
target
对象中动态的创建
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);


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

关于使用到的
Runtime


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


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

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: