利用NSProxy解決NSTimer內(nèi)存泄漏問題

問題描述:
用NSTimer來實現(xiàn)每隔一定時間執(zhí)行制定的任務,例如最常見的廣告輪播圖。如果我們在 timerWithTimeInterval:1 target:self 中指定target為當前控制器,控制器則會被timer強引用,而控制器對timer也是強引用的。一般,我們終止定時器往往在界面銷毀時,即dealloc方法中寫 [_timer invalidate];。基于上面的分析,由于循環(huán)引用的存在,控制器永遠也不會走dealloc方法,定時器會一直執(zhí)行方法,造成內(nèi)存泄露。

解決方案:利用消息轉(zhuǎn)發(fā)來斷開NSTimer對象與視圖之間的引用關系。初始化NSTimer時把觸發(fā)事件的target替換成一個單獨的對象,然后這個對象中NSTimer的SEL方法觸發(fā)時讓這個方法在當前的視圖self中實現(xiàn)。

背景知識:
NSProxy:NSProxy 是一個抽象類,它接收到任何自己沒有定義的方法他都會產(chǎn)生一個異常,所以一個實際的子類必須提供一個初始化方法或者創(chuàng)建方法,并且重載forwardInvocation:方法和methodSignatureForSelector:方法來處理自己沒有實現(xiàn)的消息。
從類名來看是代理類,專門負責代理對象轉(zhuǎn)發(fā)消息的。相比NSObject類來說NSProxy更輕量級,通過NSProxy可以幫助Objective-C間接的實現(xiàn)多重繼承的功能。

代碼:

#import <Foundation/Foundation.h>

@interface XZHProxy : NSProxy
/**
 *  代理的對象
 */
@property (nonatomic,weak)id obj;
@end
#import "XZHProxy.h"

@implementation XZHProxy

/**
 這個函數(shù)讓重載方有機會拋出一個函數(shù)的簽名,再由后面的forwardInvocation:去執(zhí)行
    為給定消息提供參數(shù)類型信息
 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSMethodSignature *sig = nil;
    sig = [self.obj methodSignatureForSelector:aSelector];
    return sig;
}

/**
 *  NSInvocation封裝了NSMethodSignature,通過invokeWithTarget方法將消息轉(zhuǎn)發(fā)給其他對象.這里轉(zhuǎn)發(fā)給控制器執(zhí)行。
 */
- (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;

/**
 *  計數(shù)
 */
@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];
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容