• NSTimer、CADisplayLink 内存泄漏


    NSTimer、CADisplayLink 内存泄漏

    内存泄漏的原因

    CADisplayLink 要用 Taget 和 Selector 初始化,NSTimer 也可以用类似的方法初始化。这样初始化之后,NSTimer 或 CADisplayLink(以下把两者统称为 CADisplayLink)会强引用 Target。当 CADisplayLink 加入 NSRunLoop 中,NSRunLoop 会强引用 CADisplayLink。直到 CADisplayLink 调用 invalidate 方法,CADisplayLink 才会被 NSRunLoop 移除,CADisplayLink 也不再强引用 Target。

    通常在 UIViewController 或 UIView 中创建 CADisplayLink,Target 是 UIViewController 或 UIView。如果只在 UIViewController 或 UIView 的 dealloc 方法中调用 CADisplayLink 的 invalidate 方法,会造成内存泄漏。因为 NSRunLoop 一直都在,CADisplayLink 不释放,Target 被强引用,Target 的 dealloc 方法不会被调用,CADisplayLink 的 invalidate 方法也不被调用,CADisplayLink 不会从 NSRunLoop 中移除。参见示意图,实线箭头表示强引用

    NSRunLoop —> CADisplayLink —> Target

    另外,为了在 Target 的 dealloc 方法中调用 CADisplayLink 的 invalidate 方法,CADisplayLink 可能作为 Target 的属性被强引用,这就形成 CADisplayLink 和 Target 的互相强引用,造成内存泄漏。

    解决方法

    改变 invalidate 方法的调用时机

    例如,在 UIViewController 的 viewDidDisappear: 方法中,或者在 CADisplayLink 实现的动画结束后,或者在其他合适的时机调用 CADisplayLink 的 invalidate 方法。这样就把 CADisplayLink 从 NSRunLoop 中移除,CADisplayLink 也不再强引用 Target。即使 Target 强引用 CADisplayLink,在 Target 被释放后,CADisplayLink 也会被释放。

    如果找不到合适的时机调用 CADisplayLink 的 invalidate 方法,那么还是在 dealloc 方法中调用 invalidate 方法,同时用 NSProxy 避免 CADisplayLink 对 Target 的强引用。开源库 FLAnimatedImage 中的 FLAnimatedImageView 用 FLWeakProxy,避免 CADisplayLink 的强引用。公开的方法只有初始化方法

    @interface FLWeakProxy : NSProxy
    
    + (instancetype)weakProxyForObject:(id)targetObject;
    
    @end
    

    使用时,把 self 套一层 FLWeakProxy 即可防止 self 被 CADisplayLink 强引用。参见示意图,虚线箭头表示弱引用

    NSRunLoop —> CADisplayLink —> Proxy - - > self

    FLWeakProxy *weakProxy = [FLWeakProxy weakProxyForObject:self];
    self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)];
    

    这样,self 的 dealloc 方法会被调用,在里面会调用 CADisplayLink 的 invalidate 方法,CADisplayLink 会被释放

    - (void)dealloc
    {
        // Removes the display link from all run loop modes.
        [_displayLink invalidate];
    }
    

    FLWeakProxy 对初始化时传入的 targetObject 进行弱引用,弱引用属性是 target。通过 Runtime 的消息转发机制 (参见 http://tech.glowing.com/cn/objective-c-runtime/) 把消息转发给 target,使 target 调用相应的方法。当 target 为空又收到消息时,把相应的方法返回值设置为空。具体代码如下

    @interface FLWeakProxy ()
    
    @property (nonatomic, weak) id target;
    
    @end
    
    
    @implementation FLWeakProxy
    
    #pragma mark Life Cycle
    
    // This is the designated creation method of an `FLWeakProxy` and
    // as a subclass of `NSProxy` it doesn't respond to or need `-init`.
    + (instancetype)weakProxyForObject:(id)targetObject
    {
        FLWeakProxy *weakProxy = [FLWeakProxy alloc];
        weakProxy.target = targetObject;
        return weakProxy;
    }
    
    
    #pragma mark Forwarding Messages
    
    - (id)forwardingTargetForSelector:(SEL)selector
    {
        // Keep it lightweight: access the ivar directly
        return _target;
    }
    
    
    #pragma mark - NSWeakProxy Method Overrides
    #pragma mark Handling Unimplemented Methods
    
    - (void)forwardInvocation:(NSInvocation *)invocation
    {
        // Fallback for when target is nil. Don't do anything, just return 0/NULL/nil.
        // The method signature we've received to get here is just a dummy to keep `doesNotRecognizeSelector:` from firing.
        // We can't really handle struct return types here because we don't know the length.
        void *nullPointer = NULL;
        [invocation setReturnValue:&nullPointer];
    }
    
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
    {
        // We only get here if `forwardingTargetForSelector:` returns nil.
        // In that case, our weak target has been reclaimed. Return a dummy method signature to keep `doesNotRecognizeSelector:` from firing.
        // We'll emulate the Obj-c messaging nil behavior by setting the return value to nil in `forwardInvocation:`, but we'll assume that the return value is `sizeof(void *)`.
        // Other libraries handle this situation by making use of a global method signature cache, but that seems heavier than necessary and has issues as well.
        // See https://www.mikeash.com/pyblog/friday-qa-2010-02-26-futures.html and https://github.com/steipete/PSTDelegateProxy/issues/1 for examples of using a method signature cache.
        return [NSObject instanceMethodSignatureForSelector:@selector(init)];
    }
    
    
    @end
    

    转载请注明出处:http://www.cnblogs.com/silence-cnblogs/p/7583289.html

  • 相关阅读:
    mysql删除重复记录语句的方法
    超详细mysql left join,right join,inner join用法分析
    CGLib动态代理原理及实现
    mysql开启慢查询日志及查询windows
    数据库事务隔离级别及脏读、幻读、不可重复读及传播行为
    java的动态代理机制详解
    MySQL常见面试题
    2019年的冬季,蒟蒻的PKUWC游记(12.20~12.23)
    初学生成函数(一)——普通生成函数
    转:基于jquery点击切换样式并且可以记忆cookie
  • 原文地址:https://www.cnblogs.com/silence-cnblogs/p/7583289.html
Copyright © 2020-2023  润新知