CADisplayLink 要用 Taget 和 Selector 初始化,NSTimer 也能夠用相似的方法初始化。這樣初始化以後,NSTimer 或 CADisplayLink(如下把二者統稱爲 CADisplayLink)會強引用 Target。當 CADisplayLink 加入 NSRunLoop 中,NSRunLoop 會強引用 CADisplayLink。直到 CADisplayLink 調用 invalidate 方法,CADisplayLink 纔會被 NSRunLoop 移除,CADisplayLink 也再也不強引用 Target。html
一般在 UIViewController 或 UIView 中建立 CADisplayLink,Target 是 UIViewController 或 UIView。若是隻在 UIViewController 或 UIView 的 dealloc 方法中調用 CADisplayLink 的 invalidate 方法,會形成內存泄漏。由於 NSRunLoop 一直都在,CADisplayLink 不釋放,Target 被強引用,Target 的 dealloc 方法不會被調用,CADisplayLink 的 invalidate 方法也不被調用,CADisplayLink 不會從 NSRunLoop 中移除。參見示意圖,實線箭頭表示強引用git
NSRunLoop —> CADisplayLink —> Targetgithub
另外,爲了在 Target 的 dealloc 方法中調用 CADisplayLink 的 invalidate 方法,CADisplayLink 可能做爲 Target 的屬性被強引用,這就造成 CADisplayLink 和 Target 的互相強引用,形成內存泄漏。objective-c
例如,在 UIViewController 的 viewDidDisappear: 方法中,或者在 CADisplayLink 實現的動畫結束後,或者在其餘合適的時機調用 CADisplayLink 的 invalidate 方法。這樣就把 CADisplayLink 從 NSRunLoop 中移除,CADisplayLink 也再也不強引用 Target。即便 Target 強引用 CADisplayLink,在 Target 被釋放後,CADisplayLink 也會被釋放。app
若是找不到合適的時機調用 CADisplayLink 的 invalidate 方法,那麼仍是在 dealloc 方法中調用 invalidate 方法,同時用 NSProxy 避免 CADisplayLink 對 Target 的強引用。開源庫 FLAnimatedImage 中的 FLAnimatedImageView 用 FLWeakProxy,避免 CADisplayLink 的強引用。公開的方法只有初始化方法ide
@interface FLWeakProxy : NSProxy + (instancetype)weakProxyForObject:(id)targetObject; @end
使用時,把 self 套一層 FLWeakProxy 便可防止 self 被 CADisplayLink 強引用。參見示意圖,虛線箭頭表示弱引用oop
NSRunLoop —> CADisplayLink —> Proxy - - > self動畫
FLWeakProxy *weakProxy = [FLWeakProxy weakProxyForObject:self]; self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)];
這樣,self 的 dealloc 方法會被調用,在裏面會調用 CADisplayLink 的 invalidate 方法,CADisplayLink 會被釋放this
- (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 爲空又收到消息時,把相應的方法返回值設置爲空。具體代碼以下atom
@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