第四十三篇、利用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];
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM