NSTimer的一般使用:

 1 @interface ViewController : UIViewController
 2 @property (nonatomic, strong) NSTimer *timer;
 3 @end
 4
 5 @implementation ViewController
 6 - (void)viewDidLoad {
 7     [super viewDidLoad];
 8     [self startTimer];
 9 }
10
11 - (void)startTimer {
12     self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTask) userInfo:nil repeats:YES];
13     [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
14 }
15
16 - (void)timerTask {
17     NSLog(@"do something");
18 }
19
20 - (void)dealloc {
21     [self.timer invalidate];
22     self.timer = nil;
23 }
24
25 @end

主要代码如上, 现在问题出来了, 当我们页面移除之后, 控制台依然一直输出 do something, 说明timer依然存在, 那么viewController还存在, 都没有被销毁, 断点dealloc发现根本不进断点.

网上查了一圈,发现如下解释:

为了保证参数的生命周期,NSTimer会对target对象retain一次,做强引用。以保证即便target销毁了,定时器还能正常调用timeEvent。因为定时器要加到RunLoop中,所以RunLoop强引用着NSTimer,一般情况下你的target就是当前的控制器,如果你想让控制器如你所愿的销毁了,首先得销毁NSTimer。不然NSTimer强引用着self,self就无法销毁,从而导致内存泄漏。
timer被schedule的时候,timer会持有target对象,NSRunLoop对象会持有timer。当invalidate被调用时,NSRunLoop对象会释放对timer的持有,timer会释放对target的持有。除此之外,没有途径可以释放timer对target的持有。所以解决内存泄露就必须撤销timer,若不撤销,target对象将永远无法释放。

最简单的解决办法:

  1. 在viewDidDisappear中调用[self.timer invalidate]来结束timer的任务;
  2. 使用block而不用target,这样肯定不会对viewController做retain;

OK, 接下来是要说的重点, 既然有最简单的解决办法, 那就有不那么简单的办法: 做一个中间代理, 用消息转发来找到对应的方法实现, 同时timer和ViewController之间不做相互强引用, 以此来解决内存无法释放的问题, 这样可以实现不在ViewController生命周期中的NSTimer的引用问题.

这个地方我们用一个官方给的中间代理类NSProxy来实现中间代理, 具体代码如下:

@interface TimerWeakProxy : NSProxy@property (nonatomic, weak) id target;+ (instancetype)proxyWithTarget:(id)target;@end@implementation TimerWeakProxy+ (instancetype)proxyWithTarget:(id)target {TimerWeakProxy *proxy = [TimerWeakProxy alloc];proxy.target = target;return proxy;
}- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {return [self.target methodSignatureForSelector:sel];
}- (void)forwardInvocation:(NSInvocation *)invocation {[invocation invokeWithTarget:self.target];
}@end

接下来就是把NSTimer的target换成中间代理:

self.timer = [NSTimer timerWithTimeInterval:1.0 target:[TimerWeakProxy proxyWithTarget:self] selector:@selector(timerTask) userInfo:nil repeats:YES];[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];

至此, 中间代理改造完毕, 其实也没有那么复杂, 重点就是NSProxy这个类, 官方的文档是这样说的:

Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy can be used to implement transparent distributed messaging (for example, NSDistantObject) or for lazy instantiation of objects that are expensive to create.

NSProxy implements the basic methods required of a root class, including those defined in the NSObject protocol. However, as an abstract class it doesn’t provide an initialization method, and it raises an exception upon receiving any message it doesn’t respond to. A concrete subclass must therefore provide an initialization or creation method and override the forwardInvocation: and methodSignatureForSelector: methods to handle messages that it doesn’t implement itself. A subclass’s implementation of forwardInvocation: should do whatever is needed to process the invocation, such as forwarding the invocation over the network or loading the real object and passing it the invocation. methodSignatureForSelector: is required to provide argument type information for a given message; a subclass’s implementation should be able to determine the argument types for the messages it needs to forward and should construct an NSMethodSignature object accordingly. See the NSDistantObject, NSInvocation, and NSMethodSignature class specifications for more information.

简单来说, 就是说这是一个抽象类, 不提供初始化方法, 需要子类来提供初始化方法, 并且需要重写forwardInvocation: 和 methodSignatureForSelector: 方法来做消息转发.

参考博文: https://www.jianshu.com/p/ee58de47fa5c

https://www.jianshu.com/p/d4589134358a

转载于:https://www.cnblogs.com/GaryZhang/p/11477189.html

通过NSProxy来解决NSTimer使用不当造成内存泄漏的问题相关推荐

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

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

  2. 解决Solaris应用程序开发内存泄漏问题

    作者: 李凌云,张一峰(laoeyu) 内存泄漏是应用软件开发过程中经常会遇到的问题,应用长期内存泄漏会占用大量操作系统内存资源,直接导致应用程序运行不稳定,严重时甚至还会影响到操作系统的正常运行.为 ...

  3. 解决Solaris应用程序开发内存泄漏问题 (1)

    作者: 李凌云,张一峰(laoeyu) 概述 内存泄漏是应用软件开发过程中经常会遇到的问题,应用长期内存泄漏会占用大量操作系统内存资源,直接导致应用程序运行不稳定,严重时甚至还会影响到操作系统的正常运 ...

  4. android释放acitity内存,Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

  5. Android内存泄漏查找和解决

    Android内存泄漏查找和解决 目录: 内存泄漏的概念 一个内存泄漏的例子 Java中"失效"的private修饰符 回头看内存泄漏例子泄漏的重点 强引用与弱引用 解决内部类的内 ...

  6. 内存泄漏的检测、定位和解决经验总结

    内存泄漏的检测.定位和解决经验总结 温辉敏(wenhm@sina.com) 2006 年 05 月 [摘要] 结合局端MCU项目中CSS.NMS模块内存泄漏检测.修正的过程,简要介绍了内存泄漏检测的工 ...

  7. android 内存泄漏分析工具,Android内存泄漏终极解决篇(上)

    一.概述 在Android的开发中,经常听到"内存泄漏"这个词."内存泄漏"就是一个对象已经不需要再使用了,但是因为其它的对象持有该对象的引用,导致它的内存不能 ...

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

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

  9. iOS之深入探究CADisplayLink和NSTimer的对比和内存溢出问题

    CADisplayLink的基本说明和使用 一.什么是CADisplayLink? 简单地说,它就是一个定时器,每隔几毫秒刷新一次屏幕. CADisplayLink是一个能让我们以和屏幕刷新率相同的频 ...

最新文章

  1. 《ActionScript 3.0权威指南》阅读笔记
  2. 图文解读:5 个刁钻的 String 面试题!
  3. 敏捷方法适合什么样的团队?
  4. php贺卡生成,用php与mysql的电子贺卡程序
  5. es6 数组合并_九个前端开发必学超级实用的 ES6 特性
  6. webservice和socket服务的区别
  7. 现在的娃娃有多智能?
  8. Linux 网络编程八(epoll应用--大并发处理)
  9. 蓝桥杯 ALGO-107 算法训练 9-7链表数据求和操作
  10. js模仿块级作用域(js没有块级作用域私有作用域)
  11. 机器学习课程笔记【十四】- 增强学习和自适应控制控制论
  12. 无线AP和无线路由器区别 wifi热点
  13. mac ios自动化 appium-doctor 安装opencv4nodejs爬坑记录
  14. 冇内容管理系统分析-js中关于array的slice和sort方法(转自JavaEye)
  15. cs224n学习笔记
  16. ue4 后期处理景深_Unreal Engine4 后期处理特效 VOL1
  17. 两台计算机通信 系统态和用户态,os2014-期末试卷A答案.pdf
  18. 【田姓】宗谱——【迁徙分布】
  19. 2021全球与中国锂一次电池市场现状及未来发展趋势
  20. css3动画制作转动相册

热门文章

  1. js实现UTC时间转为北京时间,时间戳转为时间
  2. Linux系统下如何安装软件包
  3. hdu 4263(有限制的生成树)
  4. fedora17 的 rc.local
  5. 接口访问次数_系统运行缓慢,CPU 100%,Full GC次数过多,这一招帮你全搞定
  6. snapd_snapd使管理Nextcloud变得轻而易举
  7. 以太坊公链私链_如何使用以太坊构建汽车制造供应链系统
  8. Linux操作系统(一:基本操作)
  9. android5.0后新特性修改标题头,Android5.0中Material Design的新特性
  10. 四种JOIN简单实例