美文网首页
定时器NSTimer和CADisplayLink使用的注意事项

定时器NSTimer和CADisplayLink使用的注意事项

作者: it小小菜鸟 | 来源:发表于2020-08-06 16:42 被阅读0次

    CADisplayLink、NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用。如果没有在dealloc之前主动关闭(调用invalidate),就会导致target和Timer都无法释放。
    例如:
    .m 文件生成一个timer属性

    @interface ViewController ()
    
    /** CADisplayLink */
    @property (nonatomic, strong) CADisplayLink *link;
    /** NSTimer */
    @property (nonatomic, strong) NSTimer *timer;
    @end
    

    然后这样初始化后,就会产生循环引用,导致控制器和timer都不能释放

    - (void)viewDidLoad {
        [super viewDidLoad];
        _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
    }
    
    

    解决方案:

    1、在dealloc之前主动调用invalidate方法,就会解除循环引用
    2、使用block,初始化NSTImer
    // block 可以解决循环引用问题
        __weak typeof (self) weakself = self;
        _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            [weakself timerTest];
        }];
    

    3、使用代理对象NSProxy

    1、自己写一个代理对象类,我这里用两种方法实现,一个继承自NSObject,一个继承自NSProxy。两种都可以实现,NSProxy效率更高。
    QSProxy.h

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface QSProxy : NSObject
    /** target */
    @property (nonatomic ,weak)id target;
    + (instancetype)proxyWithTarget:(id)target;
    @end
    
    
    /**
     NSProxy: 专门用来做消息转发的,效率更高。当NSProxy类对象找不到方法时,不会去父类查找,会直接调用 methodSignatureForSelector 方法进入消息转发。
    */
    @interface QSNSProxy : NSProxy
    /** target */
    @property (nonatomic ,weak)id target;
    + (instancetype)proxyWithTarget:(id)target;
    @end
    
    
    NS_ASSUME_NONNULL_END
    

    QSProxy.m

    #import "QSProxy.h"
    #import <objc/runtime.h>
    
    
    @implementation QSProxy
    + (instancetype)proxyWithTarget:(id)target
    {
        QSProxy *proxy = [[QSProxy alloc] init];
        proxy.target = target;
        return proxy;
    }
    
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        return self.target;
    }
    
    @end
    
    
    
    @implementation QSNSProxy
    
    + (instancetype)proxyWithTarget:(id)target
    {
        // NSProxy 没有init方法
        QSNSProxy *proxy = [QSNSProxy alloc];
        proxy.target = target;
        return proxy;
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
    {
        return [self.target methodSignatureForSelector:sel];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation
    {
    //    invocation.target = self.target;
    //    [invocation invoke];
        
        // 或者
        [invocation invokeWithTarget:self.target];
        
    }
    
    使用:

    这样就不会循环引用了

    _link = [CADisplayLink displayLinkWithTarget:[QSNSProxy proxyWithTarget:self] selector:@selector(linkTest)];
    [_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[QSNSProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
    // _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[QSProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
    

    在dealloc中关闭

    - (void)dealloc
    {
        [self.link invalidate];
        [_timer invalidate];
    }
    

    相关文章

      网友评论

          本文标题:定时器NSTimer和CADisplayLink使用的注意事项

          本文链接:https://www.haomeiwen.com/subject/mydzrktx.html