前言git
NSTimer的官方文檔對於target的解釋,The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated.意思就是會對target強引用,直到timer被invalidated掉。github
場景:ide
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(update:) userInfo:nil repeats:YES];控制器強引用timer,timer強引用控制器,形成循環引用,致使控制器沒法釋放形成內存泄露。oop
weakSelf:atom
__weak typeof(self) weakSelf = self;spa
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:weakSelf selector:@selector(update:) userInfo:nil repeats:YES];code
這種簡單的方式,並不能打破timer和控制器之間的循環引用,也就是說weakSelf和strongSelf在這裏的惟一區別:就是self被釋放了,target會變成nil。ip
NSProxy構建中間target:內存
DYLWeakProxy對target進行弱引用,而後經過消息轉發,最後的執行者轉發至targetObject,由於target是弱引用,所以不會形成循環引用。ci
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:[DYLWeakProxy weakProxyForObject:self] selector:@selector(update:) userInfo:nil repeats:YES];
#import <Foundation/Foundation.h> @interface DYLWeakProxy : NSProxy + (instancetype)weakProxyForObject:(id)targetObject; @end #import "DYLWeakProxy.h" @interface DYLWeakProxy () @property (weak, nonatomic) id targetObject; @end @implementation DYLWeakProxy + (instancetype)weakProxyForObject:(id)targetObject { DYLWeakProxy *weakObject = [DYLWeakProxy alloc]; weakObject.targetObject = targetObject; return weakObject; } #pragma mark - Forwarding Messages - (id)forwardingTargetForSelector:(SEL)aSelector { return self.targetObject; } #pragma mark - NSWeakProxy Method Overrides #pragma mark - Handling Unimplemented Methods - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [NSObject instanceMethodSignatureForSelector:@selector(init)]; } - (void)forwardInvocation:(NSInvocation *)invocation { void *nullPointer = NULL; [invocation setReturnValue:&nullPointer]; } @end
CADisplayLink:
一樣的,CADisplayLink也會由於強引用target,從而形成循環引用,解決這個問題與NSTimer相似,依然能夠DYLWeakProxy解決;其實加載gif的第三方FLAnimatedImage也使用該方法解決循環引用的問題。
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:[DYLWeakProxy weakProxyForObject:self] selector:@selector(update:)];
最後
除了NSTimer定時器和CADisplayLink外,GCD也能夠建立定時器(我封裝的DYLDispatchTimerButton倒計時用的就是GCD定時器),並且不存在循環應用的問題以及RunLoop的問題,NSTimer默認狀況下運行在NSDefaultRunLoopMode模式下,所以爲了防止滾動視圖在滾動過程當中NSTimer也能正常運行,會經過設置NSRunLoopCommonModes標記,自動的將NSTimer加入到帶有CommonMode標記的模式下,即NSDefaultRunLoopMode和NSEventTrackingRunLoopMode。