解决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方法是被注释的,关于这里的解释,我们可以点击这里了解。

原文地址:https://www.cnblogs.com/zbblog/p/12555322.html

时间: 2024-10-13 04:02:13

解决NSTimer或CADisplayLink计时器造成的循环引用问题。的相关文章

iOS:三种常见计时器(NSTimer、CADisplayLink、dispatch_source_t)的使用

一.介绍 在iOS中,计时器是比较常用的,用于统计累加数据或者倒计时等,例如手机号获取验证码.计时器大概有那么三种,分别是:NSTimer.CADisplayLink.dispatch_source_t 二.使用 @property (strong,nonatomic)NSTimer *timer; @property (strong,nonatomic)CADisplayLink *displaylinkTimer; @property (strong,nonatomic)dispatch_s

iOS 计时器三种定时器的用法NSTimer、CADisplayLink、GCD

原文:http://www.cocoachina.com/ios/20160919/17595.html 一.三种计时器 二.全局倒计时 #import "ViewController.h" @interface ViewController () { CADisplayLink * displaylinked; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do

用block解决nstimer循环引用

大多数开发者可能都会这样来实现定时器.创建定时器的时候,由于目标对象是self,所以要保留此实例.然而,因为定时器是用实例变量存放的,所以实例也保留了定时器,这就造成了循环引用.除非调用stop方法,或者系统回收实例,才能打破循环引用,如果无法确保stop一定被调用,就极易造成内存泄露.当指向XXClass实例的最后一个外部引用移走之后,该实例仍然会继续存活,因为定时器还保留着它.而定时器对象也不可能被系统释放,因为实例中还有一个强引用正在指向它.这种内存泄露是很严重的,如果定时器每次轮训都执行

iOS三种定时器的用法NSTimer、CADisplayLink、GCD

一,NSTimer //创建方式1 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:NO]; [timer invalidate]; //调用创建方法后,target对象的计数器会加1,直到执行完毕,自动减1.如果是循环执行的话,就必须手动关闭,否则可以不执行释放方法. //推荐-->创建方式2 NST

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

NSTimer A timer provides a way to perform a delayed action or a periodic action. The timer waits until a certain time interval has elapsed and then fires, sending a specified message to a specified object(timer就是一个能在从现在开始的未来的某一个时刻又或者周期性的执行我们指定的方法的对象)

初识iOS NSTimer 循环引用不释放问题

原文转自 :http://www.codecate.com/code/?p=77 最近开发遇到NSTimer Target 造成循环引用问题,不释放,以下是解决方案. stackoverflow上的一个解决方案 http://stackoverflow.com/questions/16821736/weak-reference-to-nstimer-target-to-prevent-retain-cycle 原文如下 Weak Reference to NSTimer Target To Pr

第四十三篇、利用NSProxy解决NSTimer内存泄漏问题

问题描述: 用NSTimer来实现每隔一定时间执行制定的任务,例如最常见的广告轮播图.如果我们在 timerWithTimeInterval:1 target:self 中指定target为当前控制器,控制器则会被timer强引用,而控制器对timer也是强引用的.一般,我们终止定时器往往在界面销毁时,即dealloc方法中写 [_timer invalidate];.基于上面的分析,由于循环引用的存在,控制器永远也不会走dealloc方法,定时器会一直执行方法,造成内存泄露. 解决方案: 利用

解决NSTimer存在的内存泄漏的问题

创建定时器会在一定的间隔后执行某些操作,一般大家会这样创建定时器,这样创建的定时,self对定时器有个引用,定时器对self也有个引用,造成了循环引用,最终造成了内存泄漏,如果定时器在做下载的操作就会一直下载. self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(startTimer) userInfo:nil repeats:YES]; 解决办法:首先创建NSTimer的

NSTimer、CADisplayLink、GCD 三种定时器的用法 —— 昉

在软件开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法.在这个时候,我们就需要用到定时器. 在iOS中有很多方法完成定时器的任务,例如 NSTimer.CADisplayLink 和 GCD都可以. 一.NSTimer 1. 创建方法 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil