NSTimer,CADisplayLink内存泄漏

作者: _Andy_ | 来源:发表于2018-09-11 17:33 被阅读6次

    今天在Q群 问了些面试题,有一个 NSTimer 怎么处理内存泄漏的问题,

    • 就是NSTimer的target被强引用了,而通常target就是所在的控制器,他又强引用的timer,造成了循环引用
      比如 平时我们一般在ViewController 添加NSTimer
      在ViewController的dealloc 方法里进行释放
    - (void)dealloc{
          [_timer invalidate];
        NSLog(@"释放%s",__func__);
    }
    

    但是deallco方法根本不会执行,除非我们在ViewController 添加事件 提前进行 [_timer invalidate]; ViewController才会执行dealloc, 当我们想让Timer一直运行直到ViewController被dealloc的时候才被释放,这就不行了。

    • 解决方案:
      在阅读YYKit的源码的时候 发现ibireme大神的 YYWeakProxy 类处理方案非常巧妙,NSTimer,CADisplayLink 都适用,使用NSProxy解决NSTimer内存泄漏问题,
      原理:
      就是生成一个临时对象弱引用回调方,以此破解强引用环。重写YYWeakProxy类的消息转发方法,保证接收方是实际回调的对象,没有形成循环引用

    YYWeakProxy

    @property (nonatomic, weak, readonly) id target;
    
    + (instancetype)proxyWithTarget:(id)target {
        return [[YYWeakProxy alloc] initWithTarget:target];
    }
    //将消息接收对象改为 _target
    - (id)forwardingTargetForSelector:(SEL)selector {
        return _target;
    }
    //self 对 target 是弱引用,一旦 target 被释放将调用下面两个方法,如果不实现的话会 crash
    - (void)forwardInvocation:(NSInvocation *)invocation {
        void *null = NULL;
        [invocation setReturnValue:&null];
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
        return [NSObject instanceMethodSignatureForSelector:@selector(init)];
    }
    

    YYWeakProxy 继承自 NSProxy,是 Foundation 框架两大基类之一,实现了 NSObject 协议。
    NSProxy 做为消息转发的抽象代理类,没有 init 方法,子类必须实现 initWithXXX: forwardInvocation: 和 methodSignatureForSelector: 方法)。
    当不能识别方法时候,就会调用forwardingTargetForSelector方法,在这个方法中,我们可以将不能识别的传递给其它对象处理
    需要重载methodSignatureForSelector和forwardInvocation的,为什么呢?因为_target是弱引用的,所以当_target可能释放了,当它被释放了的情况下,那么forwardingTargetForSelector就是返回nil了.然后methodSignatureForSelector和forwardInvocation没实现的话,就直接crash了!!!
    这也是为什么这两个方法中是随便写的 ,而没有将消息转发给其他对象的操作

    • 使用的时候是这样的
    //NSTimer,
     _timer = [NSTimer timerWithTimeInterval:1.0
                                                  target:[YYWeakProxy proxyWithTarget:self]
                                                selector:@selector(timerClick:)
                                                userInfo:nil
                                                 repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:_longPressTimer forMode:NSRunLoopCommonModes];
    
    //CADisplayLink
       _link = [CADisplayLink displayLinkWithTarget:[YYWeakProxy proxyWithTarget:self] selector:@selector(linkClick:)];
        [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    

    参考:
    IOS定时器操作和NSTimer的各种坑
    NSTimer和实现弱引用的timer的方式

    相关文章

      网友评论

        本文标题:NSTimer,CADisplayLink内存泄漏

        本文链接:https://www.haomeiwen.com/subject/yudxgftx.html