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];
}