iOS-解决NSTimer强引用

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

相关文章
相关标签/搜索