您的位置:首页 > 其它

CADisplayLink、NSTimer 循环引用及解决方法

2019-03-13 10:27 239 查看

CADisplayLink、NSTimer使用不当会产生循环引用

CADisplaylink
NSTimer
会对
target
产生强引用,如果
target
又对它们产生强引用,那么就会发生循环引用。

@interface ViewController ()

@property (strong, nonatomic) CADisplayLink *link;
@property (strong, nonatomic) NSTimer *timer;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

// 调用频率和屏幕的刷帧频率一致,60FPS
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

//    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];

//    __weak typeof(self) weakSelf = self;
//    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
//        [weakSelf timerTest];
//    }];
}

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

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

- (void)dealloc
{
NSLog(@"%s", __func__);
[self.link invalidate];
//    [self.timer invalidate];
}

@end

以上代码当控制器销毁的时候,不会走

dealloc
方法。
linkTest
方法会一直调用。
__weak typeof(self) weakSelf = self;
的解决方法只适用于
block
的情况。

解决方法

使用代理对象

NSProxy

@interface NSProxy <NSObject> {
Class	isa;
}

+ (id)alloc;
- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel;
@interface NSObject <NSObject> {
Class isa;
}

NSProxy
类是与
NSObject
类同级别的基类。并且
NSProxy
类没有
init
方法,只有
alloc
方法。

创建一个代理类继承自

NSProxy

@interface HQProxy : NSProxy
// 注意此处是weak修饰target
@property (weak, nonatomic) id target;
+ (instancetype)proxyWithTarget:(id)target;

@end

@implementation HQProxy

+ (instancetype)proxyWithTarget:(id)target
{
HQProxy *proxy = [HQProxy alloc]; // 没有init方法
proxy.target = target;
return proxy;
}

// 获取消息签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
}
// 消息转发
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.target];
}
@end

NSProxy
实例对象收到消息首先会在自己的类对象的方法列表中查找有没有对应的方法
如果没有会直接进入消息转发转发给它内部的
target
对象处理,不会再在自己的父类的方法列表中查找
这样就提高了效率。

回到

viewcontroller

- (void)viewDidLoad {
[super viewDidLoad];

// 向`[HQProxy proxyWithTarget:self]`这个对象发送`timerTest`消息,传过去的`target`是`self`。
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[HQProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
}

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

- (void)dealloc
{
NSLog(@"%s", __func__);
[self.timer invalidate];
}

测试结果不会产生循环引用。控制器销毁会进入

dealloc
方法。

流程图如下

ViewController targrtNSTimer targetNSProxy targrt
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: