解决NSTimer或CADisplayLink计时器造成的循环引用问题。
Posted zbblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决NSTimer或CADisplayLink计时器造成的循环引用问题。相关的知识,希望对你有一定的参考价值。
众所周知,我们在使用NSTimer或者CADisplayLink的时候,经常会导致引用它们的类不能正常释放,那是因为引用它们的类与它们之间产生了循环引用。看以下代码:
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES];
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(runDipslay)];
self引用了timer和displayLink,而它们又强引用了self,这样就形成了强应用。
那么如何解除这种强引用呢?
关于timer我们可以使用block的回调形式,然后在block内部引用self的时候,添加__weak即可:
__weak typeof(self) weakSelf = self; self.timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) { __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) { [timer invalidate]; return; } [strongSelf runTimer]; }];
但是CADisplayLink并没有提供block的回调方式,因此我们还有另一种解决方案,这个方案是NSTimer和CADisplayLink通用的:(原理就是利用消息转发机制)
首先我们创建一个NSProxy的子类,然后添加一个weak类型的id指针,用于存储消息转发的对象,然后实现NSProxy的消息转发机制,代码如下:
.h @interface MyTargetProxy : NSProxy + (instancetype)weakProxyTarget:(id)target; @end .m @interface MyTargetProxy () @property (nonatomic, weak) id target; @end @implementation MyTargetProxy + (instancetype)weakProxyTarget:(id)target{ MyTargetProxy *proxy = [MyTargetProxy alloc]; proxy.target = target; return proxy; } - (id)forwardingTargetForSelector:(SEL)selector{ return _target; // 将消息转发给_target对象 } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
// 因为_target示弱引用,所以可能为nil的情况,当target=nil,就会走这里,这里我们创建一个没有参数,有返回值的方法签名 return [NSObject instanceMethodSignatureForSelector:@selector(init)]; } - (void)forwardInvocation:(NSInvocation *)invocation{ void *nullPointer = NULL; [invocation setReturnValue:&nullPointer]; // 这里我们只需要返回nil即可 } @end
然后在使用NSTimer的时候如下:
MyTargetProxy *target = [MyTargetProxy weakProxyTarget:self]; self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:target selector:@selector(runTimer) userInfo:nil repeats:YES];
最后记得在self的delloc中,调用[self.timer invalidate],这样我们就完美的解决了NSTimer中的循环引用问题。
关于为啥这个MyTargetProxy要选择继承自NSProxy,我们可以点击这里了解。
另外我们可以看到NSProxy中的forwardingTargetForSelector方法是被注释的,关于这里的解释,我们可以点击这里了解。
以上是关于解决NSTimer或CADisplayLink计时器造成的循环引用问题。的主要内容,如果未能解决你的问题,请参考以下文章
iOS:三种常见计时器(NSTimerCADisplayLinkdispatch_source_t)的使用