NSTimer循环引用的几种解决方案
前言
在iOS中,NSTimer的使用是非常频繁的,但是NSTimer在使用中需要注意,避免循环引用的问题。之前经常这样写:
- (void)setupTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval: target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
}
由于self强引用了timer,同时timer也强引用了self,所以循环引用造成dealloc方法根本不会走,self和timer都不会被释放,造成内存泄漏。
下面介绍一下几种解决timer循环引用的方法。
1. 选择合适的时机手动释放timer(该方法并不太合理)
在之前自己就是这样解决循环引用的:
- 控制器中
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.timer invalidate];
self.timer = nil;
}
- view中
- (void)removeFromSuperview {
[super removeFromSuperview];
[self.timer invalidate];
self.timer = nil;
}
在某些情况下,这种做法是可以解决问题的,但是有时却会引起其他问题,比如控制器push到下一个控制器,viewDidDisappear后,timer被释放,此时再回来,timer已经不复存在了。
所以,这种"方案"并不是合理的。
2. timer使用block方式添加Target-Action
这里我们需要自己在NSTimer的分类中添加类方法:
@implementation NSTimer (BlcokTimer)
+ (NSTimer *)bl_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)(void))block repeats:(BOOL)repeats {
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(bl_blockSelector:) userInfo:[block copy] repeats:repeats];
}
+ (void)bl_blockSelector:(NSTimer *)timer {
void(^block)(void) = timer.userInfo;
if (block) {
block();
}
}
@end
通过block的方式,获取action,实际的target设置为self,即NSTimer类。这样我们在使用timer时,由于target的改变,就不再有循环引用了。 使用中还需要注意block可能引起的循环引用,所以使用weakSelf:
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer bl_scheduledTimerWithTimeInterval: block:^{
[weakSelf changeText];
} repeats:YES];
虽然没有了循环引用,但是还是应该记得在dealloc时释放timer。
3. 给self添加中间件proxy
考虑到循环引用的原因,改方案就是需要打破这些相互引用关系,因此添加一个中间件,弱引用self,同时timer引用了中间件,这样通过弱引用来解决了相互引用,如图:
接下来看看怎么实现这个中间件,直接上代码:
@interface ZYWeakObject()
@property (weak, nonatomic) id weakObject;
@end
@implementation ZYWeakObject
- (instancetype)initWithWeakObject:(id)obj {
_weakObject = obj;
return self;
}
+ (instancetype)proxyWithWeakObject:(id)obj {
return [[ZYWeakObject alloc] initWithWeakObject:obj];
}
@interface ZYWeakObject()
@property (weak, nonatomic) id weakObject;
@end
@implementation ZYWeakObject
- (instancetype)initWithWeakObject:(id)obj {
_weakObject = obj;
return self;
}
+ (instancetype)proxyWithWeakObject:(id)obj {
return [[ZYWeakObject alloc] initWithWeakObject:obj];
}
仅仅添加了weak类型的属性还不够,为了保证中间件能够响应外部self的事件,需要通过消息转发机制,让实际的响应target还是外部self,这一步至关重要,主要涉及到runtime的消息机制。
/**
* 消息转发,让_weakObject响应事件
*/
- (id)forwardingTargetForSelector:(SEL)aSelector {
return _weakObject;
} - (void)forwardInvocation:(NSInvocation *)invocation {
void *null = NULL;
[invocation setReturnValue:&null];
} - (BOOL)respondsToSelector:(SEL)aSelector {
return [_weakObject respondsToSelector:aSelector];
}
接下来就可以这样使用中间件了:
// target要设置成weakObj,实际响应事件的是self
ZYWeakObject *weakObj = [ZYWeakObject proxyWithWeakObject:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval: target:weakObj selector:@selector(changeText) userInfo:nil repeats:YES];
结论
经测试,以上两种方案都是可以解决timer的循环引用问题
代码请移步github: Demo https://github.com/zhouyangyng/timerRetainCycle
NSTimer循环引用的几种解决方案的更多相关文章
- CADisplayLink、NSTimer循环引用解决方案
前言:CADisplayLink.NSTimer 循环引用问题 CADisplayLink.NSTimer会对Target产生强引用,如果target又对他们产生强引用,那么就会引发循环引用. @ ...
- iOS容易造成循环引用的三种场景
iOS容易造成循环引用的三种场景 ARC已经出来很久了,自动释放内存的确很方便,但是并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是--循环引用.循环引用可以简单理解为 ...
- 解决NSTimer循环引用Retain Cycle问题
解决NSTimer循环引用Retain Cycle问题 iOS开发中以下的情况会产生循环引用 block delegate NSTimer 循环引用导致一些对象无法销毁,一定的情况下会对我们横须造成影 ...
- 解决NSTimer循环引用
NSTimer常见用法 @interface XXClass : NSObject - (void)start; - (void)stop; @end @implementation XXClass ...
- 【转】iOS学习之容易造成循环引用的三种场景
ARC已经出来很久了,自动释放内存的确很方便,但是并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是——循环引用.循环引用可以简单理解为A引用了B,而B又引用了A,双方都同 ...
- 【原】iOS容易造成循环引用的三种场景,就在你我身边!
ARC已经出来很久了,自动释放内存的确很方便,但是并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是——循环引用.循环引用可以简单理解为A引用了B,而B又引用了A,双方都同 ...
- iOS-容易造成循环引用的三种场景
ARC已经出来很久了,自动释放内存的确很方便,但是并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是——循环引 用.循环引用可以简单理解为A引用了B,而B又引用了A,双方都 ...
- iOS 容易引“起循环引用”的三种场景
笔者在阅读中总结了一下,在iOS平台容易引起循环引用的四个场景: 一.parent-child相互持有.委托模式 [案例]: @interface FTAppCenterMainViewContr ...
- 用block解决nstimer循环引用
大多数开发者可能都会这样来实现定时器.创建定时器的时候,由于目标对象是self,所以要保留此实例.然而,因为定时器是用实例变量存放的,所以实例也保留了定时器,这就造成了循环引用.除非调用stop方法, ...
随机推荐
- 主成分分析PCA详解
转载请声明出处:http://blog.csdn.net/zhongkelee/article/details/44064401 一.PCA简介 1. 相关背景 上完陈恩红老师的<机器学习与知识 ...
- 使用libpcap过滤arp
上一篇博客简单讲述了libpcap的工作流程及简单使用,今天我们需要做的是继续使用libpcap抓取我们感兴趣的流量,并进行简单的解析: 测试环境是centos 7 下面贴一张arp帧结构图: 下面我 ...
- CentOS7快速搭建LNMP环境
名词解释: LNMP:Linux+Nginx+MySql+PHPLAMP:LInux+Apache+MySql+PHPNginx的正确读法应该是Engine X我们使用CentOS自带的YUM来安装 ...
- 安装VirtualBox中的增强功能包VBoxLinuxAdditions
首先,增强功能包VBoxLinuxAdditions有什么作用呢?请看: (1)实现客户机和主机间的鼠标切换. (2)与主机实现文件共享. (3)自动调整客户机分辨率. (4)与主机共享剪贴板的内容. ...
- Scrapy 1.4 文档 02 安装指南
安装 Scrapy Scrapy 运行在 Python 2.7 和 Python 3.3 或更高版本上. 如果您使用的是 Anaconda 或 Miniconda,则可以从 conda-forge 通 ...
- idea 15安装步骤2017.6.25
http://www.jetbrains.com/idea/下载路径 http://www.jb51.net/softjc/549047.html 安装教程 http://www.cnblogs.c ...
- spring创建bean及数据注入
通过spring的IoC可以实现由配置文件来创建类的对象,可以降低类鱼类之间的耦合, 通常我们都是在代码中控制对象的生成和属性注入,而使用IoC后,就可以将设计好的类交给IoC容器,让容器去控制对象的 ...
- selenium相关技术研究(从1.0-3.0)
注: 以下内容引自http://www.cnblogs.com/hhudaqiang/p/6550135.html Selenium相关技术研究(从1.0-3.0) 好吧,最近看wxpython有点多 ...
- log.go 源码阅读
) //打开文件 权限:文件追加 文件创建 文件读写 if err != nil { return err } level, err := logrus.Par ...
- dummy_backend_queue.go
) } func (d *dummyBackendQueue) Empty() error { return nil }