zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    面向对象的继承关系体现在数据结构上时,如何表示
    codeforces 584C Marina and Vasya
    codeforces 602A Two Bases
    LA 4329 PingPong
    codeforces 584B Kolya and Tanya
    codeforces 584A Olesya and Rodion
    codeforces 583B Robot's Task
    codeforces 583A Asphalting Roads
    codeforces 581C Developing Skills
    codeforces 581A Vasya the Hipster
  • 原文地址:https://www.cnblogs.com/silence-cnblogs/p/7583289.html
Copyright © 2011-2022 走看看