iOS中解决NSTimer循环引用的三种方式

图片描述

逆水行舟 不进则退

今天有个人来公司面试,问了他平时在使用Timer定时器时怎么解决循环引用的问题。
然后就得到了这样一个答案:

    __weak typeof(self) weakSelf = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(fire) userInfo:nil repeats:YES];

这种方式不能解决循环引用的原因是:在NSTimer的内部会对当前的weakSelf引用计数+1

声明变量

    @property (nonatomic, strong, nullable) NSObject *target;
    @property (nonatomic, strong, nullable) NSTimer *timer;

第一种:NSTimer提供的API

使用NSTimer提供的API,在block中执行定时任务

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

引用逻辑
self强引用timer弱引用target

第二种: 借助runtime给对象添加消息处理的能力

引用逻辑
self强引用timer强引用target

    _target = [[NSObject alloc] init];
    class_addMethod([_target class], @selector(fire), class_getMethodImplementation([self class], @selector(fire)), "v@:");
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:_target selector:@selector(fire) userInfo:nil repeats:YES];

第三种:通过消息转发的方法的方式

创建一个集成自NSProxy的类PHJProxy 声明一个target

    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    
    @interface PHJProxy : NSProxy
    
    @property (nonatomic, weak) id target;
    
    @end

PHJProxy的实现

    @implementation PHJProxy
    // 发送给target
    - (void)forwardInvocation:(NSInvocation *)invocation {
        [invocation invokeWithTarget:self.target];
    }
    // 给target注册一个方法签名
    - (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
        return [self.target methodSignatureForSelector:sel];
    }
    
    @end

PHJProxy 和 NSTimer的使用

    self.proxy = [PHJProxy alloc];
    self.proxy.target = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self.proxy selector:@selector(fire) userInfo:nil repeats:YES];

引用逻辑
self强引用timer强引用proxy弱引用self

阅读 902更新于 6月29日
推荐阅读
目录