NSTimer常见用法

 1 @interface XXClass : NSObject
 2 - (void)start;
 3 - (void)stop;
 4 @end
 5
 6 @implementation XXClass {
 7     NSTimer *timer;
 8 }
 9
10 - (id)init {
11     return [super init];
12 }
13
14 - (void)dealloc {
15     [timer]
16 }
17
18 - (void)stop {
19     [timer invalidate];
20     timer = nil;
21 }
22
23 - (void)start {
24     timer = [NSTimerscheduledTimerWithTimeInterval:5.0
25                                             target:self
26                                           selector:selector(doSomething)
27                                           userInfo:nil
28                                            repeats:YES];
29 }
30
31 - (void)doSomething {
32     //doSomething
33 }
34
35 @end

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

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

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

Block解决循环引用

 1 @interface NSTimer (XXBlocksSupport)
 2
 3 + (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
 4                                          block:(void(^)())block
 5                                        repeats:(BOOL)repeats;
 6
 7 @end
 8
 9 @implementation NSTimer (XXBlocksSupport)
10
11 + (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
12                                          block:(void(^)())block
13                                        repeats:(BOOL)repeats
14 {
15     return [self scheduledTimerWithTimeInterval:interval
16                                           target:self
17                                         selector:@selector(xx_blockInvoke:)
18                                         userInfo:[block copy]
19                                          repeats:repeats];
20 }
21
22 + (void)xx_blockInvoke:(NSTimer *)timer {
23     void (^block)() = timer.userinfo;
24     if(block) {
25         block();
26     }
27 }
28
29 @end
30 //调用
31 - (void)start {
32     __weak XXClass *weakSelf = self;
33     timer = [NSTimer xx_scheduledTimerWithTimeInterval:.5
34                                                  block:^{
35                                                  XXClass *strongSelf = weakSelf;
36                                                  [strongSelf doSomething];
37                                                         }
38                                                repeats:YES];
39 }

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

weakProxy解决循环引用

NSProxy

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

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

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

消息转发机制

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

1 //消息转发第一步,在这里可以动态的为类添加方法,这样类自己就能处理了
2 +resolveInstanceMethod:
3 //消息转发第二步,在第一步无法完成的情况下执行。这里只是把一个Selector简单的转发给另一个对象
4 - forwardingTargetForSelector:
5 //消息转发第三步,在第二步也无法完成的情况下执行。将整个消息封装成NSInvocation,传递下去
6 - 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:1target:[WeakProxy proxyWithTarget:self]selector:@selector(timerInvoked:)userInfo:nilrepeats:YES];

原理如下:

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

Reference:

1.用Block解决NSTimer循环引用

2.NSProxy与消息转发机制

转载于:https://www.cnblogs.com/H7N9/p/6540578.html

解决NSTimer循环引用相关推荐

  1. nstimer循环引用_解决NSTimer循环引用导致内存泄漏的六种方法

    demo放在了GitHub 内存泄漏的原因: self强引用timer.timer添加在runloop上,只要timer不销毁self就销毁不了.当然了你可以选择在viewWillDisappear中 ...

  2. nstimer循环引用_iOS中解决NSTimer循环引用问题

    NSTimer使用不当就会造成内存泄漏,比如常见的使用方法: //定义 @property (nonatomic, strong) NSTimer *timer; //实现 self.timer = ...

  3. nstimer循环引用_ios开发中经典循环引用场景?

    1.属性传值循环引用 如:- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexP ...

  4. nstimer循环引用_NSTimer循环引用的几种解决方案

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

  5. nstimer循环引用_NSTimer定时器进阶——详细介绍,循环引用分析与解决

    引言 定时器:A timer waits until a certain time interval has elapsed and then fires, sending a specified m ...

  6. nstimer循环引用_警惕使用NSTimer时的循环引用

    使用NSTimer可能会碰到循环引用的问题.特别是当类具有NSTimer类型的成员变量,并且需要反复执行计时任务时.例如 _timer = [NSTimer scheduledTimerWithTim ...

  7. nstimer循环引用_iOS容易造成循环引用的三种场景NSTimer以及对应的使用方法(一)...

    对,这就是NSTimer , 1.NSTimer会retain你添加调用方法的对象 2.NSTimer是要加到runloop中才会起作用 3.NSTimer会并不是准确的按照你指定的时间触发的 4.N ...

  8. 相亲app开发,解决内存循环引用的问题

    循环引用是什么 ARC已经出来很久了,自动释放内存的确很方便,但是在相亲app开发应用中,并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是--循环引用.循环引用可以简单理 ...

  9. 解决flask循环引用的问题

    刚开始结构是这样的 app - init.py - models.py init.py是这样的 ` from flask import Flask from flask-sqlalchemy impo ...

  10. NSTimer循环引用的问题

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

最新文章

  1. 15个超实用的php正则表达式
  2. 今日头条极速版怎样签到_今日头条投放广告的费用多少钱?今日头条广告投放完整流程是怎样?...
  3. 【Paper】2021_Distributed Consensus Tracking of Networked Agent Systems Under Denial-of-Service Attack
  4. NTU 课程笔记:Nonparametric statistics
  5. python自动抢红包软件_快过年啦,还怕手速慢,我用Python自动抢红包!
  6. PageRank行将过时 搜索引擎遭遇范式转移
  7. Optional.isEmpty()在JDK 11 EA构建中可用
  8. [渝粤教育] 广东-国家-开放大学 21秋期末考试个人与团队管理10257k2
  9. day 02 python 基础
  10. mybatis学习(50):嵌套查询
  11. Spring @Transactional注解浅谈
  12. Win10 OPNET14.5+VS2010 安装教程
  13. 如何画一条0.5px的边(细线)
  14. 各大CMS采集资源站合集
  15. 导入Excel至数据库中 外部表不是预期格式错误信息
  16. Bot 崛起:你的企业需要考虑这11个重要问题
  17. ESP32-S2 st7789 SPI TFT彩屏240X320
  18. 解决[W pthreadpool-cpp.cc:90] Warning: Leaking Caffe2 thread-pool after fork. (function pthreadpool)
  19. JAVA基础——第二章,变量,数据类型和运算符
  20. A1,A2,A3,A4纸的尺寸大小|A4纸与分辨率的关系|像素换算|ABC号纸尺寸|纸张幅面规格

热门文章

  1. 求解偏微分方程开源有限元软件deal.II学习--Step 11
  2. JSP弹出窗口和模式对话框
  3. 解决 IDEA 无法找到 java.util.Date 的问题
  4. C#读写三菱Fx PLC 使用Fx 串口协议 读写Fx3U设备
  5. SPOJ-LCS Longest Common Substring
  6. linux 使用systemctl 启动服务报错: Error: No space left on device
  7. Phaser中的组对象group
  8. [: -ge: unary operator expected 错误
  9. py2exe将python打包成exe
  10. nosql数据库MongoDB的用法