zoukankan      html  css  js  c++  java
  • NSTimer打破循环引用的几种方式

    timerWithTimeInterval创建出来的timer无法立刻使用,需要添加到NSRunloop中才可以正常工作, 相当于runloop强持有timer, timer又强持有self, 导致无法释放

    一: 手动销毁定时器

    但存在一些弊端, 比如push到下个页面时,当前页面仍在内存中,定时器仍在计时,来回关闭开启很麻烦

    在viewWillDisappear 

    [self.timer invalidate];
    self.timer = nil;

    或pop时在didMoveToParentViewController中

        if (nil == parent) {
            [self.timer invalidate];
            self.timer = nil;
        }

    二: 自定义定时器, 添加target和selector, timer对self弱引用

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface TimerWrapper : NSObject
    
    - (id) initWithTimerInterval:(NSTimeInterval)interval target:(id)target selector:(SEL) sel;
    
    - (void) stop;
    
    @end
    
    
    
    #import "TimerWrapper.h"
    
    @interface TimerWrapper()
    
    @property (nonatomic, weak) id target;
    @property (nonatomic, assign) SEL sel;
    @property (nonatomic, strong) NSTimer* timer;
    
    @end
    
    @implementation TimerWrapper
    
    - (id) initWithTimerInterval:(NSTimeInterval)interval target:(id)target selector:(SEL) sel {
        if (self = [super init]) {
            self.target = target;
            self.sel = sel;
            self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
        }
        return self;
    }
    
    - (void) fire {
        if ([self.target respondsToSelector:self.sel]) {
            [self.target performSelector:self.sel];
        }
    }
    
    - (void) stop {
        [self.timer invalidate];
        self.timer = nil;
    }
    
    
    #import "TZViewController.h"
    #import "TimerWrapper.h"
    
    @interface TZViewController ()
    //@property (nonatomic, strong) NSTimer* timer;
    @property (nonatomic, strong) TimerWrapper* timer;
    
    @end
    
    @implementation TZViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
      
        /// RunLoop -> timer -> target --> self
        self.timer = [[TimerWrapper alloc] initWithTimerInterval:1.0 target:self selector:@selector(fire)];
    //    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
    }
    
    
    //- (void) didMoveToParentViewController:(UIViewController *)parent {
    //    if (nil == parent) {
    //        [self.timer invalidate];
    //        self.timer = nil;
    //    }
    //}
    
    
    - (void) fire {
        NSLog(@"%s", __func__);
    }
    
    - (void)dealloc
    {
        [self.timer stop];
        NSLog(@"%s", __func__);
    }

    三: 使用NSProxy, 借助一个虚基类NSProxy,(NSProxy其主要用来消息转发的处理, 自定义或者YYWeakProxy

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface WeakProxy : NSProxy
    
    @property (nonatomic, weak) id target;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    #import "WeakProxy.h"
    
    @implementation WeakProxy
    
    //获取当前的方法签名
    - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
        return [self.target methodSignatureForSelector:sel];
    }
    
    
    //指定当前消息的处理者
    - (void) forwardInvocation:(NSInvocation *)invocation {
        [invocation invokeWithTarget:self.target];
    }
    
    @end
    
    
    
    #import "TZViewController.h"
    #import "WeakProxy.h"
    
    @interface TZViewController ()
    
    //@property (nonatomic, strong) id target;
    @property (nonatomic, strong) NSTimer* timer;
    @property (nonatomic, strong) WeakProxy* proxy;
    
    
    @end
    
    @implementation TZViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
     
        //虚基类只有alloc方法,所以初始化,直接调用alloc
        self.proxy = [WeakProxy alloc];
    //当前Proxy的target设为当前的self,因为真正要处理消息的其实是当前的viewcontroller(其实这个target就相当于delegate)
        self.proxy.target = self;
        
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self.proxy selector:@selector(fire) userInfo:nil repeats:YES];
    }
    
    
    - (void) fire {
        NSLog(@"%s", __func__);
    }
    
    - (void)dealloc
    {
        [self.timer invalidate];
        self.timer = nil;
        NSLog(@"%s", __func__);
    }
    
    
    
    @end

    四: 引入中间者, 利用runtime, 动态添加方法, 将强引用转移到target上

    #import "TZViewController.h"
    #import <objc/runtime.h>
    
    @interface TZViewController ()
    
    @property (nonatomic, strong) id target;
    @property (nonatomic, strong) NSTimer* timer;
    
    
    @end
    
    @implementation TZViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
      
        /// RunLoop -> timer -> target ---> self
        self.target = [NSObject new];
        class_addMethod([self.target class], @selector(fire), class_getMethodImplementation([self class], @selector(fire)), "v@:");
        
        
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self.proxy selector:@selector(fire) userInfo:nil repeats:YES];
    }
    
    
    - (void) fire {
        NSLog(@"%s", __func__);
    }
    
    - (void)dealloc
    {
        [self.timer invalidate];
        self.timer = nil;
        NSLog(@"%s", __func__);
    }
    
    @end

    五: 系统高于10.0时, 使用带block的timer, 兼容10.0以下可给NSTimer分类添加方法

    __weak typeof(self) weakSelf = self;
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
            __strong typeof(self) strongSelf = weakSelf;
            [strongSelf fire];
        }];
    首先我们创建一个NSTimer的分类,
    //  NSTimer+ZHTimer.h
    #import <Foundation/Foundation.h>
     
    @interface NSTimer (ZHTimer)
    +(NSTimer *)zh_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(void))block;
    @end
     
    //  NSTimer+ZHTimer.m
    #import "NSTimer+ZHTimer.h"
     
    @implementation NSTimer (ZHTimer)
    +(NSTimer *)zh_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(void))block
    {
        return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(zh_blockHandle:) userInfo:block repeats:YES]; 
     
    //这里面这个self,其实指的是当前的类对象,在内存中只存在一份,就是以单例的形式存在,所以我们在每一次创建实例变量都要通过这个类对象来创建,
    //所以并不需要担心当前的target造成循环引用,因为单例不需要被释放,只有当APP被Q的时候,存在内存中的单例才会被释放掉。
    }
     
    +(void)zh_blockHandle:(NSTimer *)timer{
        void(^block)(void) = timer.userInfo;
        if (block) {
            block();
        }
    }
     
    @end
     
    //调用
    __weak typeof(self) weakSelf = self;
    _timer = [NSTimer zh_scheduledTimerWithTimeInterval:1.0f repeats:YES block:^{
            __strong typeof(self) strongSelf = weakSelf;
            [strongSelf fire];
        }];
     
  • 相关阅读:
    实战:垂直电商如何撬动“女人腰包”
    谈谈项目收尾
    项目管理心得:一个项目经理的个人体会、经验总结
    IT项目经理沟通技巧的重要性
    项目跟踪:项目跟踪要跟踪什么呢?
    会员营销,你真的做到了吗?
    Git入门——基础知识问答
    文摘:威胁建模(STRIDE方法)
    写在2015年工作的第一天
    简化工作——我的bat文件
  • 原文地址:https://www.cnblogs.com/jiefangzhe/p/15119874.html
Copyright © 2011-2022 走看看