我们通常使用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.mHJProxy1 *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