前言: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循环引用解决方案的更多相关文章

  1. iOS 循环引用解决方案

    一.BLOCK 循环引用 一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身.构成循环引用. // 定义 block 的时候,会对外部变量做一次 cop ...

  2. Atitit.json xml 序列化循环引用解决方案json

    Atitit.json xml 序列化循环引用解决方案json 1. 循环引用1 2. 序列化循环引用解决方法1 2.1. 自定义序列化器1 2.2. 排除策略1 2.3. 设置序列化层次,一般3级别 ...

  3. 解决NSTimer循环引用Retain Cycle问题

    解决NSTimer循环引用Retain Cycle问题 iOS开发中以下的情况会产生循环引用 block delegate NSTimer 循环引用导致一些对象无法销毁,一定的情况下会对我们横须造成影 ...

  4. 解决NSTimer循环引用

    NSTimer常见用法 @interface XXClass : NSObject - (void)start; - (void)stop; @end @implementation XXClass ...

  5. NSTimer循环引用的几种解决方案

    前言 在iOS中,NSTimer的使用是非常频繁的,但是NSTimer在使用中需要注意,避免循环引用的问题.之前经常这样写: - (void)setupTimer { self.timer = [NS ...

  6. 用block解决nstimer循环引用

    大多数开发者可能都会这样来实现定时器.创建定时器的时候,由于目标对象是self,所以要保留此实例.然而,因为定时器是用实例变量存放的,所以实例也保留了定时器,这就造成了循环引用.除非调用stop方法, ...

  7. NSTimer循环引用的问题

    前言: 记得之前看过一个面试题问:ARC环境下的dealloc方法有什么用?问题解答是:代理指针置空,停止定时器timer,注销通知,释放掉实例变量.看着没什么问题,而且网上一收也是大概这样的答案.今 ...

  8. OC MRC之循环引用问题(代码分析)

    // // main.m // 07-循环引用 // // Created by apple on 13-8-9. // Copyright (c) 2013年 itcast. All rights ...

  9. iOS 里面 NSTimer 防止 循环引用

    使用NSTimer的类 #import "TBTimerTestObject.h" #import "TBWeakTimerTarget.h" @interfa ...

随机推荐

  1. Infrastructure 知识: dnf对module的处理

    引言 从RHEL8/CentOS8开始,dnf取代yum作为rpm 包管理工具.与之而来的还有模块(module)这个东西. 有了它们的加持,让在同一个OS上安装不同版本的软件或者开发语言的工作比之前 ...

  2. 【ACM程序设计】最小生成树 Prim算法

    最小生成树 ● 最小生成树的定义是给定一个无向图,如果它任意两个顶点都联通并且是一棵树,那么我们就称之为生成树(Spanning Tree).如果是带权值的无向图,那么权值之和最小的生成树,我们就称之 ...

  3. 腾讯tbs 内存泄露

    一.背景 TBS(腾讯浏览服务)是腾讯提供的移动端webview体验的整套解决方案(https://x5.tencent.com/docs/index.html),可以用于移动端加载doc.xls.p ...

  4. mysql外键,锁

    mysql外键: 场景:用于建立两个表之间的联系,让A表中一个字段,可以在另一个表中字段值的范围去查找 注意事项: (1)被参照表和参照表字段属性必须一致 (2)参照表必须设置主键 (3)必须选择支持 ...

  5. 【万字长文】使用 LSM Tree 思想实现一个 KV 数据库

    目录 设计思路 何为 LSM-Treee 参考资料 整体结构 内存表 WAL SSTable 的结构 SSTable 元素和索引的结构 SSTable Tree 内存中的 SSTable 数据查找过程 ...

  6. 探究MySQL中SQL查询的成本

    成本 什么是成本,即SQL进行查询的花费的时间成本,包含IO成本和CPU成本. IO成本:即将数据页从硬盘中读取到内存中的读取时间成本.通常1页就是1.0的成本. CPU成本:即是读取和检测是否满足条 ...

  7. Hadoop入门学习笔记(二)

    Yarn学习 YARN简介 YARN是一个通用资源管理系统和调度平台,可为上层应用提供统一的资源管理和调度 YARN功能说明 资源管理系统:集群的硬件资源,和程序运行相关,比如内存.CPU等. 调度平 ...

  8. 带你学习MindSpore中算子使用方法

    摘要:本文分享下MindSpore中算子的使用和遇到问题时的解决方法. 本文分享自华为云社区<[MindSpore易点通]算子使用问题与解决方法>,作者:chengxiaoli. 简介 算 ...

  9. 图的连通性--Tarjan算法

    一些概念 无向图: 连通图:在无向图中,任意两点都直接或间接连通,则称该图为连通图.(或者说:任意两点之间都存在可到达的路径) 连通分量: G的 最大连通子图 称为G的连通分量. 有向图 (ps.区别 ...

  10. 牛客多校赛2K Keyboard Free

    Description 给定 \(3\) 个同心圆,半径分别为 \(r1,r2,r3\) ,三个点分别随机分布在三个圆上,求这个三角形期望下的面积. Solution 首先可以固定 \(A\) 点,枚 ...