zoukankan      html  css  js  c++  java
  • 第四十三篇、利用NSProxy解决NSTimer内存泄漏问题

    问题描述:
      用NSTimer来实现每隔一定时间执行制定的任务,例如最常见的广告轮播图。如果我们在 timerWithTimeInterval:1 target:self 中指定target为当前控制器,控制器则会被timer强引用,而控制器对timer也是强引用的。一般,我们终止定时器往往在界面销毁时,即dealloc方法中写 [_timer invalidate];。基于上面的分析,由于循环引用的存在,控制器永远也不会走dealloc方法,定时器会一直执行方法,造成内存泄露。

    解决方案:

      利用消息转发来断开NSTimer对象与视图之间的引用关系。初始化NSTimer时把触发事件的target替换成一个单独的对象,然后这个对象中NSTimer的SEL方法触发时让这个方法在当前的视图self中实现。

    背景知识:
      NSProxy:NSProxy 是一个抽象类,它接收到任何自己没有定义的方法他都会产生一个异常,所以一个实际的子类必须提供一个初始化方法或者创建方法,并且重载forwardInvocation:方法和methodSignatureForSelector:方法来处理自己没有实现的消息。
    从类名来看是代理类,专门负责代理对象转发消息的。相比NSObject类来说NSProxy更轻量级,通过NSProxy可以帮助Objective-C间接的实现多重继承的功能。

    代码:

    #import <Foundation/Foundation.h>
    
    @interface XZHProxy : NSProxy
    /**
     *  代理的对象
     */
    @property (nonatomic,weak)id obj;
    @end
    #import "XZHProxy.h"
    
    @implementation XZHProxy
    
    /**
     这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行
        为给定消息提供参数类型信息
     */
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
        NSMethodSignature *sig = nil;
        sig = [self.obj methodSignatureForSelector:aSelector];
        return sig;
    }
    
    /**
     *  NSInvocation封装了NSMethodSignature,通过invokeWithTarget方法将消息转发给其他对象.这里转发给控制器执行。
     */
    - (void)forwardInvocation:(NSInvocation *)anInvocation{
        [anInvocation invokeWithTarget:self.obj];
    }
    @end
    #import "ViewController.h"
    #import "XZHProxy.h"
    @interface ViewController ()
    /**
     *代理
     */
    @property (nonatomic,strong)XZHProxy *proxy;
    
    /**
     *  定时器
     */
    @property (nonatomic,strong)NSTimer *timer;
    
    /**
     *  计数
     */
    @property (nonatomic,assign)NSInteger count;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        self.proxy = [XZHProxy alloc];
        //作为当前控制器的代理
        self.proxy.obj = self;
        //target为代理
        self.timer = [NSTimer timerWithTimeInterval:1 target:self.proxy selector:@selector(timerEvent) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    }
    
    - (void)timerEvent{
        NSLog(@"%zd",self.count++);
    }
    
    - (void)dealloc{
        NSLog(@"----dealloc");
        //释放计时器
        [self.timer invalidate];
    }
  • 相关阅读:
    ios swift 判断uiviewcontroller时push present 进来的 还是pop进来的
    vue wangeditor3封装
    ios uiimagepickercontroller 选择相册或者拍照上传
    ios 监控键盘状态
    ios 真机使用相机闪退问题
    swift bannerview 广告轮播图
    ios avplayer 监控播放进度
    【C++】智能指针
    [LeetCode] Word Break
    【海量数据处理】100亿个整数,内存足够,如何找到中位数?内存不足,如何找到中位数?
  • 原文地址:https://www.cnblogs.com/HJQ2016/p/5928801.html
Copyright © 2011-2022 走看看