我们通常使用NSTimer或CADisplayLink会使用以下方式
//定义
@property (nonatomic, strong)NSTimer *timer;
//实现
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:proxy selector:@selector(showMsg) userInfo:nil repeats:YES];
//销毁
-(void)dealloc
{
[self.timer invalidate];
}
因为控制器强引用了timer,timer又强引用了控制器,所以会产生循环引用。
尝试解决办法:
1、把timer改成弱引用
@property (nonatomic, weak)NSTimer *timer;
虽然控制器对timer是弱引用,但是控制的delloc方法依赖于timer的invalidate,timer的invalidate又依赖于控制器的delloc方法,依旧是循环引用;
2、使用 __weak
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:weakSelf selector:@selector(showMsg) userInfo:nil repeats:YES];
weak 关键字适用于block,当block引用了块外的变量时,会根据修饰变量的关键字来决定是强引用还是弱引用,如果变量使用weak关键字修饰,那block会对变量进行弱引用,如果没有__weak关键字,那就是强引用。
但是NSTimer的 scheduledTimerWithTimeInterval:target 方法内部不会判断修饰target的关键字,其内部会对target进行强引用,还是会产生循环引用。
最终解决办法
循环引用产生的原因就是因为A强引用B,同时B强引用A,那如果我们在A和B之间加一个C,让A强引用C,C弱引用B,B强引用A,那在B被释放后,A也会释放,就可以解决循环引用的问题;
解决方法1:
定义一个继承自NSObject的类HJProxy,在HJProxy中弱引用target
//HJProxy.h
@interface HJProxy : NSObject
+(instancetype)proxyWithTarget:(id)target;
@end
//HJProxy.m
#import "HJProxy.h"
@interface HJProxy()
@property (weak, nonatomic) id target;
@end
@implementation HJProxy
+(instancetype)proxyWithTarget:(id)target
{
HJProxy *proxy = [[HJProxy alloc] init];
proxy.target = target;
return proxy;
}
//使用消息转发,将消息转发给控制器
-(id)forwardingTargetForSelector:(SEL)aSelector
{
return self.target;
}
@end
//ViewController.m
HJProxy1 *proxy = [HJProxy1 proxyWithTarget:self];
//将timer的target设置为proxy,proxy又弱引用了控制器,其实最终还是调用了控制器的showMsg方法。
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:proxy selector:@selector(showMsg) userInfo:nil repeats:YES];
解决方法2:
使用iOS的NSProxy类,NSProxy就是专门用来做消息转发的.
//HJProxy1.h
@interface HJProxy1 : NSProxy
+(instancetype)proxyWithTarget:(id)target;
@end
//HJProxy1.m
@interface HJProxy1()
@property (weak, nonatomic)id target;
@end
@implementation HJProxy1
+(instancetype)proxyWithTarget:(id)target
{
HJProxy1 *proxy = [HJProxy1 alloc];
proxy.target = target;
return proxy;
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
}
-(void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.target];
}
@end