CADisplayLink、NSTimer循环引用解决方案

博客 动态
0 208
优雅殿下
优雅殿下 2022-07-08 09:01:29
悬赏:0 积分 收藏

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内可以解决循环引用的问题。

? 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;
posted @ 2022-07-08 08:55 任淏 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员