CADisplayLink、NSTimer 循环引用及解决方法
2019-03-13 10:27
239 查看
CADisplayLink、NSTimer使用不当会产生循环引用
CADisplaylink、
NSTimer会对
target产生强引用,如果
target又对它们产生强引用,那么就会发生循环引用。
@interface ViewController () @property (strong, nonatomic) CADisplayLink *link; @property (strong, nonatomic) NSTimer *timer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 调用频率和屏幕的刷帧频率一致,60FPS self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)]; [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; // self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES]; // __weak typeof(self) weakSelf = self; // self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) { // [weakSelf timerTest]; // }]; } - (void)timerTest { NSLog(@"%s", __func__); } - (void)linkTest { NSLog(@"%s", __func__); } - (void)dealloc { NSLog(@"%s", __func__); [self.link invalidate]; // [self.timer invalidate]; } @end
以上代码当控制器销毁的时候,不会走
dealloc方法。
linkTest方法会一直调用。
__weak typeof(self) weakSelf = self;的解决方法只适用于
block的情况。
解决方法
使用代理对象
NSProxy。
@interface NSProxy <NSObject> { Class isa; } + (id)alloc; - (void)forwardInvocation:(NSInvocation *)invocation; - (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel;
@interface NSObject <NSObject> { Class isa; }
NSProxy类是与
NSObject类同级别的基类。并且
NSProxy类没有
init方法,只有
alloc方法。
创建一个代理类继承自
NSProxy。
@interface HQProxy : NSProxy // 注意此处是weak修饰target @property (weak, nonatomic) id target; + (instancetype)proxyWithTarget:(id)target; @end @implementation HQProxy + (instancetype)proxyWithTarget:(id)target { HQProxy *proxy = [HQProxy alloc]; // 没有init方法 proxy.target = target; return proxy; } // 获取消息签名 - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.target methodSignatureForSelector:sel]; } // 消息转发 - (void)forwardInvocation:(NSInvocation *)invocation { [invocation invokeWithTarget:self.target]; } @end
NSProxy实例对象收到消息首先会在自己的类对象的方法列表中查找有没有对应的方法
如果没有会直接进入消息转发转发给它内部的
target对象处理,不会再在自己的父类的方法列表中查找
这样就提高了效率。
回到
viewcontroller
- (void)viewDidLoad { [super viewDidLoad]; // 向`[HQProxy proxyWithTarget:self]`这个对象发送`timerTest`消息,传过去的`target`是`self`。 self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[HQProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES]; } - (void)timerTest { NSLog(@"%s", __func__); } - (void)dealloc { NSLog(@"%s", __func__); [self.timer invalidate]; }
测试结果不会产生循环引用。控制器销毁会进入
dealloc方法。
流程图如下
ViewController targrtNSTimer targetNSProxy targrt相关文章推荐
- NSProxy解决CADisplayLink ,NSTimer强引用target引起的无法释放问题
- CADisplayLink以及和NSTimer的区别
- NSTimer和CADisplayLink的基本用法
- iOS开发中深入理解CADisplayLink和NSTimer
- Swift学习记录 -- 14.闭包的使用和解决循环引用方法
- swift中解决循环引用的方法
- 深入理解CADisplayLink和NSTimer
- 雪花飘落 - 定时器(NSTimer/CADisplayLink)
- iOS NSTimer循环引用的几种解决办法
- modules分开和循环引用的解决方法
- EF生成的实体类.序列化循环引用的解决方法
- NSTimer,dispatch_time_t, CADisplayLink(含示例代码)
- 当tableview/scrollview滚动时定时器NSTimer / CADisplayLink停止响应问题
- iOS 面试题~避免循环引用~解决方法
- [转]NSTimer和CADisplayLink的基本用法
- IOS学习之—— 定时器 NSTimer 和 CADisplayLink 类的使用
- EntityFramework Model有外键时,Json提示循环引用 解决方法
- CADisplayLink 与 NSTimer 有什么不同
- 解决NSTimer循环引用Retain Cycle问题
- iOS开发--计时器-NSTimer与CADisplayLink