内存管理——定时器问题

内存管理系列文章

内存管理—MRC时代的手动内存管理
内存管理—weak的实现原理
内存管理——autorelease原理分析
内存管理——定时器问题
iOS程序的内存布局


CADisplayLink、NSTimer的循环引用问题

CADisplayLinkQuartzCore框架下的的一种定时器,用在跟画图相关的处理当中。NSTimer大家应该很熟悉,是我们最常用的定时器。这两种定时器分别提供如下两个API

+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

这两个API里面都有target参数,该target会被CADisplayLink/NSTimer强引用。如果CADisplayLink或者NSTimer作为属性被一个视图控制器VC强引用,当我们在调用上述两个API的时候,target参数传VC,这样VC和CADisplayLink/NSTimer之间便会形成引用循环,无法释放,造成内存泄漏。图示如下NSTimer/CADisplayLink产生循环引用

NSTimer的解决方案1
通过使用别的API来添加NSTimer,如

 (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

并且将self通过__weak typeof(self) weakSelf == self;包装成弱指针,传入其中即可。

NSTimer的解决方案2
通过增加一个中间代理对象来打破引用循环。请看下图
如上图所示,在timerVC之间增加一个代理对象otherObjecttimer的强指针target指向otherObjectotherObject的弱指针target指向VC,这样就成功打破了引用循环。我们之所以需要借助第三者来破环,是因为NSTimer并非开源,我们无法修改其内部target的强弱性。因此只能通过一个自定义的代理对象来做一层引用中转,最终打破引用循环。

现在还有一个细节需要处理,增加代理对象otherObject之前,是由timer通过target直接调用VC里面的定时器方法的。现在中间多了一层otherObject,该如何实现定时器方法的调用呢?其实方法蛮多的,相信大家都能想出一些解决方案。这里就直接推荐一种比较巧妙的方法——通过消息转发。如下图代理对象的消息转发
因为代理对象的本质目的,就是打破引用循环,并且传递方法,了解OC消息机制的原理前提下,你应该很好理解消息转发的作用,正好可以巧妙的用在这个场景下。请好好体会一下。

下面是一份代码案例

#import "ViewController.h"
#import "CLProxy.h"

@interface ViewController ()
//@property (nonatomic, strong) CADisplayLink *link;
@property (nonatomic, strong) NSTimer *timer;
@end

@implementation ViewController

- (void)viewDidLoad {
   
    [super viewDidLoad];

    //CADisplayLink用来保证调用频率和屏幕的刷帧频率一致,60FPS
//    self.link = [CADisplayLink displayLinkWithTarget:[CLProxy proxyWithTarget:self] selector:@selector(linkTest)];
//    [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[CLProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值