前言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。