前言: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. 【转】python代码优化常见技巧

    https://blog.csdn.net/egefcxzo3ha1x4/article/details/97844631

  2. Node.js躬行记(18)——半吊子的可视化搭建系统

    我们组维护的管理后台会接到很多开发需求,每次新开页面,就会到处复制黏贴相关代码. 并且还会经常性的翻阅文档,先在书签或地址栏输入WIKI地址,然后找到那一份说明文档,再定位到要看的组件位置. 虽然单人 ...

  3. Django-----cookie&session

    cookie 保存在用户浏览器端的一个键值对(别人给的凭证) 服务端可以向用户浏览器写cookie 客户端每次发请求会携带cookie去(放在请求头里面) 淘宝的cookie  京东的cookie(h ...

  4. Django视图函数:CBV与FBV (ps:补充装饰器)

    CBV 基于类的视图  FBV 基于函数的视图 CBV: 1 项目目录下: 2 urlpatterns = [ 3 path('login1/',views.Login.as_view()) #.as ...

  5. python 本地配置文件库 Dynaconf 简介

    [前言] 在项目中经常会遇到以下几种需要用到配置文件的场景: 相同的配置参数用在不同的代码中,如果需要调整,则需要手动将各个使用到的地方都相应调整. 密码等信息不想硬编码在项目文件中. 配置文件的格式 ...

  6. [笔记] $f(i)$ 为 $k$ 次多项式,$\sum_{i=0}^nf(i)\cdot q^i$ 的 $O(k\log k)$ 求法

    \(f(i)\) 为 \(k\) 次多项式,\(\sum_{i=0}^nf(i)\cdot q^i\) 的 \(O(k\log k)\) 求法 令 \(S(n)=\sum_{i=0}^{n-1}f(i ...

  7. learnByWork

    2019.5.5(移动端页面) 1.页面的整体框架大小min-width: 300px~max-width: 560px: 2.具体大小单位用px: 3.网页布局用div不是table,在特殊情况,如 ...

  8. .NET混合开发解决方案14 WebView2的基本身份验证

    系列目录     [已更新最新开发文章,点击查看详细] WebView2控件应用详解系列博客 .NET桌面程序集成Web网页开发的十种解决方案 .NET混合开发解决方案1 WebView2简介 .NE ...

  9. jenkins插件Role-based添加账号后显示红色"No type prefix"

    jenkins插件Role-based添加账号save后,前面显示红色"No type prefix",不影响使用. 查了下原因,网上很少正解,我这里记录下正确的方法: 添加用户: ...

  10. Java 17 新特性:switch的模式匹配(Preview)

    还记得Java 16中的instanceof增强吗? 通过下面这个例子再回忆一下: Map<String, Object> data = new HashMap<>(); da ...