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 ...
随机推荐
- Oracle 常用运维命令整理
点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 一.oracle建库与删库命令 (1)oracle11g ...
- 没错,华为开始对IoT下手了!
最近,有很多粉丝在后台私信 想知道目前最热的技术是什么? 小编觉得,5G时代到来 物联网技术将迎来快速的发展 加上目前,国内物联网人才短缺 每年人才缺口达百万 IoT物联网将成为最热门的技术 最近,小 ...
- 操作系统实现-boot.asm实现
博客网址:www.shicoder.top 微信:18223081347 欢迎加群聊天 :452380935 这一次我们进入操作系统实现的真实编码, 这一次主要是完善对boot.asm文件的全部实现, ...
- 翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6
翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6 原文地址:https://devblogs.microsoft.com/dotnet/upgrading-a-wcf-service-t ...
- 【Rust】使用HashMap解决官方文档中的闭包限制
问题概述 值缓存是一种更加广泛的实用行为,我们可能希望在代码中的其他闭包中也使用他们.然而,目前 Cacher 的实现存在两个小问题,这使得在不同上下文中复用变得很困难. 第一个问题是 Cacher ...
- 实战派 | Java项目中玩转Redis6.0客户端缓存!
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是Hydra. 在前面的文章中,我们介绍了Redis6.0中的新特性客户端缓存client-side caching,通过tel ...
- spring boot 统一接口异常返回值
创建业务 Exception 一般在实际项目中,推荐创建自己的 Exception 类型,这样在后期会更容易处理,也比较方便统一,否则,可能每个人都抛出自己喜欢的异常类型,而造成代码混乱 Servic ...
- Java获取特定区间随机数及产生不重复随机数
问题 有这样一种需求,在这样一个数组中String[] arr = new String[]{"电商", "互联网", "小程序", &qu ...
- Git技法:.gitignore、移除暂存与撤销修改
1. .gitignore常见项目添加 1.1 .gitignore模板 .gitignore针对每个语言都有对应的模板,在GitHub创建项目时就可以选择(你可以在GitHub提供的.gitigno ...
- 《Mybatis 手撸专栏》第9章:细化XML语句构建器,完善静态SQL解析
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你只是在解释过程,而他是在阐述高度! 如果不是长时间的沉淀.积累和储备,我一定也没有 ...