定时器NSTimer和CADisplayLink使用的注意事项

CADisplayLink、NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用。如果没有在dealloc之前主动关闭(调用invalidate),就会导致target和Timer都无法释放。
例如:
.m 文件生成一个timer属性

@interface ViewController ()

/** CADisplayLink */
@property (nonatomic, strong) CADisplayLink *link;
/** NSTimer */
@property (nonatomic, strong) NSTimer *timer;
@end

然后这样初始化后,就会产生循环引用,导致控制器和timer都不能释放

- (void)viewDidLoad {
    [super viewDidLoad];
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
}

解决方案:

1、在dealloc之前主动调用invalidate方法,就会解除循环引用
2、使用block,初始化NSTImer
// block 可以解决循环引用问题
    __weak typeof (self) weakself = self;
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        [weakself timerTest];
    }];

3、使用代理对象NSProxy

1、自己写一个代理对象类,我这里用两种方法实现,一个继承自NSObject,一个继承自NSProxy。两种都可以实现,NSProxy效率更高。
QSProxy.h

#import 

NS_ASSUME_NONNULL_BEGIN

@interface QSProxy : NSObject
/** target */
@property (nonatomic ,weak)id target;
+ (instancetype)proxyWithTarget:(id)target;
@end


/**
 NSProxy: 专门用来做消息转发的,效率更高。当NSProxy类对象找不到方法时,不会去父类查找,会直接调用 methodSignatureForSelector 方法进入消息转发。
*/
@interface QSNSProxy : NSProxy
/** target */
@property (nonatomic ,weak)id target;
+ (instancetype)proxyWithTarget:(id)target;
@end


NS_ASSUME_NONNULL_END

QSProxy.m

#import "QSProxy.h"
#import 


@implementation QSProxy
+ (instancetype)proxyWithTarget:(id)target
{
    QSProxy *proxy = [[QSProxy alloc] init];
    proxy.target = target;
    return proxy;
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.target;
}

@end



@implementation QSNSProxy

+ (instancetype)proxyWithTarget:(id)target
{
    // NSProxy 没有init方法
    QSNSProxy *proxy = [QSNSProxy alloc];
    proxy.target = target;
    return proxy;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    return [self.target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
//    invocation.target = self.target;
//    [invocation invoke];
    
    // 或者
    [invocation invokeWithTarget:self.target];
    
}
使用:

这样就不会循环引用了

_link = [CADisplayLink displayLinkWithTarget:[QSNSProxy proxyWithTarget:self] selector:@selector(linkTest)];
[_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[QSNSProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
// _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[QSProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];

在dealloc中关闭

- (void)dealloc
{
    [self.link invalidate];
    [_timer invalidate];
}

你可能感兴趣的:(定时器NSTimer和CADisplayLink使用的注意事项)