CADisplayLink、NSTimer循环引用解决方案
前言:CADisplayLink、NSTimer 循环引用问题
CADisplayLink、NSTimer会对Target产生强引用,如果target又对他们产生强引用,那么就会引发循环引用。
@interface ViewController ()
@property (nonatomic, strong) CADisplayLink *link;
@property (nonatomic, strong) NSTimer *time;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 保证调用频率和刷帧频率 60fps
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode] ;
self.time = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timeTest) userInfo:nil repeats:YES];
}
- (void)timeTest {
NSLog(@"%s", __func__);
}
- (void)linkTest {
NSLog(@"%s", __func__);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%s", __func__);
[self.time invalidate];
}
__weak typeof(self) weakSelf = self;能否解决NSTimer的循环引用问题?
答:我们并不能够通过__weak typeof(self) weakSelf = self;代码来实现解决循环引用。
__weak typeof(self) weakSelf = self;是用在block内可以解决循环引用的问题。
当
方案一、使用 NSTimer 使用 block(CADisplayLink 没有提供block api 不可以用)
NSTime 使用提供 block 的 API ,通过 __weak 解决循序引用问题
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.time = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakSelf timeTest];
} ];
}
- (void)timeTest {
NSLog(@"%s", __func__);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%s", __func__);
[self.time invalidate];
}
方案二、使用 NSObject 消息转发
通过中间变量,是中间的一个引用变成弱引用。
第一步:创建继承 NSObject 的 RHMiddleTarget 中间文件,创建 target 属性,使用 weak 弱引用修饰
#import <Foundation/Foundation.h>
@interface RHMiddleTarget : NSObject
@property (nonatomic, weak) id target;
+ (instancetype)middleTargetWithTarget:(id)target;
@end
#import "RHMiddleTarget.h"
@implementation RHMiddleTarget
+ (instancetype)middleTargetWithTarget:(id)target {
RHMiddleTarget *middleTarget = [[RHMiddleTarget alloc] init];
middleTarget.target = target;
return middleTarget;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
// 返回可以处理的对象, 给对象发送aSelector消息
// objc_msgSend(self.target, aSelector);
return self.target;
}
@end
第二步:在使用 CADisplayLink 和 NSTimer 中的 target 传到中间变量里:
// 保证调用频率和刷帧频率 60fps
self.link = [CADisplayLink displayLinkWithTarget:[RHMiddleTarget middleTargetWithTarget:self] selector:@selector(linkTest)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode] ;
self.time = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[RHMiddleTarget middleTargetWithTarget:self] selector:@selector(timeTest) userInfo:nil repeats:YES];
第三步:测试验证:
2022-07-05 16:01:47.895716+0800 Interview03-定时器[10828:260303] -[ViewController timeTest]
2022-07-05 16:01:48.895649+0800 Interview03-定时器[10828:260303] -[ViewController timeTest]
2022-07-05 16:01:49.895571+0800 Interview03-定时器[10828:260303] -[ViewController timeTest]
2022-07-05 16:01:50.666056+0800 Interview03-定时器[10828:260303] -[ViewController touchesBegan:withEvent:]
方案三、使用 NSProxy 消息转发
第一步:创建继承 NSObject 的 RHMiddleTarget 中间文件,创建 target 属性,使用 weak 弱引用修饰
#import <Foundation/Foundation.h>
@interface RHMiddleTarget : NSProxy
@property (nonatomic, weak) id target;
+ (instancetype)middleTargetWithTarget:(id)target;
@end
#import "RHMiddleTarget.h"
@implementation RHMiddleTarget
+ (instancetype)middleTargetWithTarget:(id)target {
RHMiddleTarget *middleTarget = [RHMiddleTarget alloc];
middleTarget.target = target;
return middleTarget;
}
// 返回方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
// 进行相应的调用
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
第二步:在使用 CADisplayLink 和 NSTimer 中的 target 传到中间变量里
// 保证调用频率和刷帧频率 60fps
self.link = [CADisplayLink displayLinkWithTarget:[RHMiddleTarget middleTargetWithTarget:self] selector:@selector(linkTest)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode] ;
self.time = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[RHMiddleTarget middleTargetWithTarget:self] selector:@selector(timeTest) userInfo:nil repeats:YES];
第三步:测试验证
2022-07-05 16:27:25.427454+0800 Interview03-定时器[11550:281823] -[ViewController linkTest]
2022-07-05 16:27:25.444101+0800 Interview03-定时器[11550:281823] -[ViewController linkTest]
2022-07-05 16:27:25.456983+0800 Interview03-定时器[11550:281823] -[ViewController touchesBegan:withEvent:]
方案四、使用 viewWillDisappear
在控制器销毁或者定时器停止的时候,调用如下的方法
[self.timer invalidate];
[self.link invalidate];
self.timer = nil;
self.link = nil;
CADisplayLink、NSTimer循环引用解决方案的更多相关文章
- iOS 循环引用解决方案
一.BLOCK 循环引用 一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身.构成循环引用. // 定义 block 的时候,会对外部变量做一次 cop ...
- Atitit.json xml 序列化循环引用解决方案json
Atitit.json xml 序列化循环引用解决方案json 1. 循环引用1 2. 序列化循环引用解决方法1 2.1. 自定义序列化器1 2.2. 排除策略1 2.3. 设置序列化层次,一般3级别 ...
- 解决NSTimer循环引用Retain Cycle问题
解决NSTimer循环引用Retain Cycle问题 iOS开发中以下的情况会产生循环引用 block delegate NSTimer 循环引用导致一些对象无法销毁,一定的情况下会对我们横须造成影 ...
- 解决NSTimer循环引用
NSTimer常见用法 @interface XXClass : NSObject - (void)start; - (void)stop; @end @implementation XXClass ...
- NSTimer循环引用的几种解决方案
前言 在iOS中,NSTimer的使用是非常频繁的,但是NSTimer在使用中需要注意,避免循环引用的问题.之前经常这样写: - (void)setupTimer { self.timer = [NS ...
- 用block解决nstimer循环引用
大多数开发者可能都会这样来实现定时器.创建定时器的时候,由于目标对象是self,所以要保留此实例.然而,因为定时器是用实例变量存放的,所以实例也保留了定时器,这就造成了循环引用.除非调用stop方法, ...
- NSTimer循环引用的问题
前言: 记得之前看过一个面试题问:ARC环境下的dealloc方法有什么用?问题解答是:代理指针置空,停止定时器timer,注销通知,释放掉实例变量.看着没什么问题,而且网上一收也是大概这样的答案.今 ...
- OC MRC之循环引用问题(代码分析)
// // main.m // 07-循环引用 // // Created by apple on 13-8-9. // Copyright (c) 2013年 itcast. All rights ...
- iOS 里面 NSTimer 防止 循环引用
使用NSTimer的类 #import "TBTimerTestObject.h" #import "TBWeakTimerTarget.h" @interfa ...
随机推荐
- MySQL Router重装后重新连接集群进行引导出现的——此主机中之前已配置过的问题
问题出现的前因: 因为重新安装了MySQL Router,然后打算重新连接上目标集群进行MySQL Router的初始化引导,结果报错了! [root@linux666 system]# mysqlr ...
- OSPF MTU问题
OSFP(开放式最短路径优先)链路状态协议,IGP 1.mtu 检测 链路俩段不匹配 假设双方的mtu不一致时 ospf建立如下: R1与R2交互hello报文,其中包含:目的IP地址:224.0.0 ...
- Python学习-Day1(Typora软件与计算机)
学习总括 Typora软件介绍(markdown语法) 相关拓展知识 文件的后缀名是什么? 什么是语言? 什么是编程语言? 什么是编程?(程序员写代码的本质) 计算机的五大组成部分 计算机的本质 计算 ...
- 5 分钟教你快速掌握 GitHub Actions 自动部署博客
自从 GitHub 宣布 GitHub Actions 在平台上对所有开发人员和存储库可用以来,GitHub Actions 越来越受欢迎.很多第三方平台在生态系统中有速度等限制,将进一步推动开发人员 ...
- HCNP Routing&Switching之Super VLAN
前文我们了解了VLAN隔离技术MUX VLAN相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16196936.html:今天我们来聊一聊VLAN优化S ...
- 为什么vue中的v-bind用在class属性上有点怪?
如图所见,普通的标签属性我们直接使用 :属性名=`props的值` 就可以了.为什么用到class里要加[ ] 这个???
- Apache Shiro 漏洞汇总
Apache Shiro 漏洞汇总 以下是我个人通过收集信息收集起来的一些Apache Shiro漏洞信息,这些漏洞的poc都是公开的,利用起来也是比较简单 Apache Shiro是什么东西: Ap ...
- 溢出属性,定位,z-index,JS
溢出属性 1.visible(默认值):使溢出内容展示 2.hidden:隐藏溢出内容且不出现滚动条 3.scroll:隐藏溢出容器的内容,溢出的内容可以通过滚动呈现 4.auto:与scroll没啥 ...
- 谈谈markdown
谈谈markdown 欢迎关注我的博客,️点他即可. 最近一年开始学习有关编程的内容了. 迷上代码的我开始接触到一些好玩的东西,我发现很多事情都可以由代码来完成,甚至是ppt.同学就经常说我疯掉了,连 ...
- netty系列之:netty中常用的对象编码解码器
目录 简介 什么是序列化 重构序列化对象 序列化不是加密 使用真正的加密 使用代理 Serializable和Externalizable的区别 netty中对象的传输 ObjectEncoder O ...