NSTimer常见用法

 @interface XXClass : NSObject
- (void)start;
- (void)stop;
@end @implementation XXClass {
NSTimer *timer;
} - (id)init {
return [super init];
} - (void)dealloc {
[timer]
} - (void)stop {
[timer invalidate];
timer = nil;
} - (void)start {
timer = [NSTimerscheduledTimerWithTimeInterval:5.0
target:self
selector:selector(doSomething)
userInfo:nil
repeats:YES];
} - (void)doSomething {
//doSomething
} @end

创建定时器的时候,由于目标对象是self,所以要保留此实例。然而,因为定时器是用实例变量存放的,所以实例也保留了定时器,这就造成了循环引用。除非调用stop方法,或者系统回收实例,才能打破循环引用,如果无法确保stop一定被调用,就极易造成内存泄露。

当指向XXClass实例的最后一个外部引用移走之后,该实例仍然会继续存活,因为定时器还保留着它。而定时器对象也不可能被系统释放,因为实例中还有一个强引用正在指向它。这种内存泄露是很严重的,如果定时器每次轮训都执行一些下载工作,常常会更容易导致其他内存泄露问题。

针对于此,有人想到利用block来避免这种循环应用。

Block解决循环引用

 @interface NSTimer (XXBlocksSupport)

 + (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats; @end @implementation NSTimer (XXBlocksSupport) + (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(xx_blockInvoke:)
userInfo:[block copy]
repeats:repeats];
} + (void)xx_blockInvoke:(NSTimer *)timer {
void (^block)() = timer.userinfo;
if(block) {
block();
}
} @end
//调用
- (void)start {
__weak XXClass *weakSelf = self;
timer = [NSTimer xx_scheduledTimerWithTimeInterval:.
block:^{
XXClass *strongSelf = weakSelf;
[strongSelf doSomething];
}
repeats:YES];
}

定时器现在的target是NSTimer类对象,这是个单例,此处依然有类对象的循环引用.下面介绍更好的解决方式weakProxy。

weakProxy解决循环引用

NSProxy

NSProxy本身是一个抽象类,它遵循NSObject协议,提供了消息转发的通用接口。NSProxy通常用来实现消息转发机制和惰性初始化资源。

使用NSProxy,你需要写一个子类继承它,然后需要实现init以及消息转发的相关方法。

 //当一个消息转发的动作NSInvocation到来的时候,在这里选择把消息转发给对应的实际处理对象
- (void)forwardInvocation:(NSInvocation *)anInvocation //当一个SEL到来的时候,在这里返回SEL对应的NSMethodSignature
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector //是否响应一个SEL
+ (BOOL)respondsToSelector:(SEL)aSelector

消息转发机制

消息转发涉及到三个核心方法

 //消息转发第一步,在这里可以动态的为类添加方法,这样类自己就能处理了
+resolveInstanceMethod:
//消息转发第二步,在第一步无法完成的情况下执行。这里只是把一个Selector简单的转发给另一个对象
- forwardingTargetForSelector:
//消息转发第三步,在第二步也无法完成的情况下执行。将整个消息封装成NSInvocation,传递下去
- forwardInvocation:

消息转发机制使得代码变的很灵活:一个类本身可以完全不实现某些方法,它只要能转发就可以了。

WeakProxy来实现弱引用

@interface WeakProxy : NSProxy
@property (weak,nonatomic,readonly)id target;
+ (instancetype)proxyWithTarget:(id)target;
- (instancetype)initWithTarget:(id)target;
@end @implementation WeakProxy
- (instancetype)initWithTarget:(id)target{
_target = target;
return self;
}
+ (instancetype)proxyWithTarget:(id)target{
return [[self alloc] initWithTarget:target];
}
- (void)forwardInvocation:(NSInvocation *)invocation{
SEL sel = [invocation selector];
if ([self.target respondsToSelector:sel]) {
[invocation invokeWithTarget:self.target];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
return [self.target methodSignatureForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector{
return [self.target respondsToSelector:aSelector];
}
@end

外部创建Timer

  self.timer = [NSTimer timerWithTimeInterval:
target:[WeakProxy proxyWithTarget:self]
selector:@selector(timerInvoked:)
userInfo:nil
repeats:YES];

原理如下:

我们把虚线处变成了弱引用。于是,Controller就可以被释放掉,我们在Controller的dealloc中调用invalidate,就断掉了Runloop对Timer的引用,于是整个三个淡蓝色的就都被释放掉了。

Reference:

1.用Block解决NSTimer循环引用

2.NSProxy与消息转发机制

解决NSTimer循环引用的更多相关文章

  1. 解决NSTimer循环引用Retain Cycle问题

    解决NSTimer循环引用Retain Cycle问题 iOS开发中以下的情况会产生循环引用 block delegate NSTimer 循环引用导致一些对象无法销毁,一定的情况下会对我们横须造成影 ...

  2. 用block解决nstimer循环引用

    大多数开发者可能都会这样来实现定时器.创建定时器的时候,由于目标对象是self,所以要保留此实例.然而,因为定时器是用实例变量存放的,所以实例也保留了定时器,这就造成了循环引用.除非调用stop方法, ...

  3. NSTimer循环引用的几种解决方案

    前言 在iOS中,NSTimer的使用是非常频繁的,但是NSTimer在使用中需要注意,避免循环引用的问题.之前经常这样写: - (void)setupTimer { self.timer = [NS ...

  4. CADisplayLink、NSTimer循环引用解决方案

    前言:CADisplayLink.NSTimer 循环引用问题 ​ CADisplayLink.NSTimer会对Target产生强引用,如果target又对他们产生强引用,那么就会引发循环引用. @ ...

  5. 解决FastJson循环引用的问题

    temp 本来被循环引用,运行方法报错. 解决方法:对temp进行处理 SerializerFeature feature = SerializerFeature.DisableCircularRef ...

  6. Json.net对于导航属性的处理(解决对象循环引用)

    对于两张表A.B多对多的关系中,A的导航属性中有B,B的导航属性中有A,这样Json.net对A或者B对象序列化时会形成死循环 所以对于导航属性要加标签 首先在A.B实体类工程(Model)中引用Js ...

  7. 05解决flask循环引用的问题

    1, 什么是循环引用问题?为什么会导致循环引用? 1.1先讲是什么? 主文件中class类过多会导致主文件冗余,如下图,所以我们单独给class类一个文件,然后再引用它. 1.2再讲为什么? 主文件为 ...

  8. NSTimer循环引用的问题

    前言: 记得之前看过一个面试题问:ARC环境下的dealloc方法有什么用?问题解答是:代理指针置空,停止定时器timer,注销通知,释放掉实例变量.看着没什么问题,而且网上一收也是大概这样的答案.今 ...

  9. 深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用(上)

    深入研究Block捕获外部变量和__block实现原理 前言 在上篇中,仔细分析了一下Block的实现原理以及__block捕获外部变量的原理.然而实际使用Block过程中,还是会遇到一些问题,比如R ...

随机推荐

  1. 【转载】每天一个Linux命令

    目  录 每天一个linux命令(1)  : ls 命令 每天一个linux命令(2)  : cd 命令 每天一个linux命令(3)  : pwd 命令 每天一个linux命令(4)  : mkdi ...

  2. framework7 1.3.5 路由跳转后DOM失效问题

    再这个版本的7会存在一个问题,那就是loadpage到指定页面后才做其中的DOM比如DIV里面的text或者HTML,虽然控制台会显示改变后的值但是页面上却还是原值,这时候需要改变方法使用reload ...

  3. JS二维数组的写法以及注意事项

    最终数组:"line":[ { "Name":"WK_CT", "Sex":"CT", " ...

  4. 请教Nutzwk项目,在beetl页面怎么用shiro标签呢?

    请教Nutzwk项目,在beetl页面怎么用shiro标签呢?  发布于 381天前  作者 WenTao-Love  195 次浏览  复制  上一个帖子  下一个帖子  标签: nutzwk 如题 ...

  5. node入口文件分析和目录初始化

    1.需要安装的模块 npm install express npm install jade npm install mongoose npm install bower -g npm install ...

  6. GDB调试手册[转]

    Linux 包含了一个叫gdb 的GNU 调试程序.gdb 是一个用来调试C和C++程序的强力调试器.它使你能在程序运行时观察程序的内部结构和内存的使用情况.以下是 gdb 所提供的一些功能:它使你能 ...

  7. 缓冲区溢出实战教程系列(二):dev c++编译汇编代码

    小伙伴们对我上一篇文章的反应完全出乎了我的意料,感谢大家对我的支持和认可.接下来我会精心的把这一系列课程设计好,尽量详细的展示给大家.上篇文章我列举了一个缓冲区溢出的小例子,并提到了dev c++.o ...

  8. B3942 Censoring

    爆炸入口 有一个S串和一个T串,长度均小于1,000,000,设当前串为U串,然后从前往后枚举S串一个字符一个字符往U串里添加,若U串后缀为T,则去掉这个后缀继续流程. 这道题确乎是个很好的联系kmp ...

  9. 【洛谷P3390】矩阵快速幂

    矩阵快速幂 题目描述 矩阵乘法: A[n*m]*B[m*k]=C[n*k]; C[i][j]=sum(A[i][1~n]+B[1~n][j]) 为了便于赋值和定义,我们定义一个结构体储存矩阵: str ...

  10. 页面js中文乱码解决

    <script src="../Content/kindeditor-4.1.5/kindeditor.js" charset="utf-8" >& ...