Shun 发表于 2022-7-19 16:26:49

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 {
    ;

    // 保证调用频率和刷帧频率 60fps
    self.link = ;
    forMode:NSDefaultRunLoopMode] ;
   
    self.time = ;
}

- (void)timeTest {
    NSLog(@"%s", __func__);
}

- (void)linkTest {
    NSLog(@"%s", __func__);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"%s", __func__);
    ;
}

__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 {
    ;
   
    __weak typeof(self) weakSelf = self;
    self.time = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
      ;
    } ];
}

- (void)timeTest {
    NSLog(@"%s", __func__);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"%s", __func__);
    ;
}
方案二、使用 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 = [ 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 = selector:@selector(linkTest)];
    forMode:NSDefaultRunLoopMode] ;
   
    self.time = selector:@selector(timeTest) userInfo:nil repeats:YES];
第三步:测试验证:
2022-07-05 16:01:47.895716+0800 Interview03-定时器 -
2022-07-05 16:01:48.895649+0800 Interview03-定时器 -
2022-07-05 16:01:49.895571+0800 Interview03-定时器 -
2022-07-05 16:01:50.666056+0800 Interview03-定时器 -
方案三、使用 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 = ;
    middleTarget.target = target;
    return middleTarget;
}

// 返回方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return ;
}

// 进行相应的调用
- (void)forwardInvocation:(NSInvocation *)invocation {
    ;
}

第二步:在使用 CADisplayLink 和 NSTimer 中的 target 传到中间变量里
    // 保证调用频率和刷帧频率 60fps
    self.link = selector:@selector(linkTest)];
    forMode:NSDefaultRunLoopMode] ;
   
    self.time = selector:@selector(timeTest) userInfo:nil repeats:YES];
第三步:测试验证
2022-07-05 16:27:25.427454+0800 Interview03-定时器 -
2022-07-05 16:27:25.444101+0800 Interview03-定时器 -
2022-07-05 16:27:25.456983+0800 Interview03-定时器 -
方案四、使用 viewWillDisappear
​在控制器销毁或者定时器停止的时候,调用如下的方法
;
;
self.timer = nil;
self.link = nil;


   
   
   
                        

https://www.cnblogs.com/r360/p/16456965.html
页: [1]
查看完整版本: CADisplayLink、NSTimer循环引用解决方案