iOS NSTimer遇坑整理

一、NSTimer使用

@interface UIViewController ()

@property (nonatomic, strong) NSTimer *timer;

@end

/*

? * repeats:參數(shù)表示是否重復(fù)執(zhí)行(YES表示每1秒運(yùn)行一次function方法。NO表示不重復(fù)只調(diào)用 一次,timer運(yùn)行一次就會(huì)自動(dòng)停止運(yùn)行)

/*

self.timer?=??[NSTimer?scheduledTimerWithTimeInterval:1.0?target:self?selector:@selector(fire)?userInfo:nil?repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:self.timerforMode:NSRunLoopCommonModes];

注意:將計(jì)數(shù)器的repeats設(shè)置為YES的時(shí)候,self的引用計(jì)數(shù)會(huì)加1。因此可能會(huì)導(dǎo)致self(即viewController)不能release,所以在UIViewController delloc前將計(jì)數(shù)器timer設(shè)置為失效,否則可能會(huì)導(dǎo)致內(nèi)存泄露。

//開啟定時(shí)器

[self.timer fire];

//取消定時(shí)器(這個(gè)是永久的停止)

[self.timer?invalidate];

self.timer?=?nil;

注意:停止后,一定要將timer賦空,否則還是沒有釋放

//暫停定時(shí)器(然后再某種情況下再次開啟運(yùn)行timer)

self.timer.fireDate = [NSDate distantFuture];

//再次開啟定時(shí)器

self.timer.fireDate = [NSDate distantPast];

通常寫到這會(huì)遇到諸多問題

二、NSTimer常見問題

1、當(dāng)repeats為YES時(shí)timer出現(xiàn)無(wú)法釋放的問題原因:(強(qiáng)引用,而非循環(huán)引用引起)

//runloop強(qiáng)引用timer,timer強(qiáng)引用self。如果timer不失效,self就不會(huì)釋放,進(jìn)而造成內(nèi)存泄漏

runloop -> timer -> self(UIViewController)

2、解決辦法有4種:

1>也是最low的方法在控制器消失的時(shí)候(viewDidDisappear方法中)設(shè)置timer失效,但是會(huì)出現(xiàn)一些問題,跳轉(zhuǎn)下一級(jí)界面的時(shí)候timer失效,再返回的時(shí)候還得在界面出現(xiàn)(viewWillAppear方法中)時(shí)從新設(shè)置timer,比較繁瑣,控制不好還會(huì)問題。不推薦使用

2>在UIViewController中調(diào)用didMoveToParentViewController:方法設(shè)置timer失效

注意:這種方法只有在進(jìn)入VC時(shí)使用的push的方式進(jìn)入才有效,present進(jìn)入不會(huì)調(diào)用此方法

- (void)didMoveToParentViewController:(UIViewController *)parent{

? ? ? ? if (!parent) {

? ? ? ? ? ? ? [self.timer invalidate];

? ? ? ? ? ? ? self.timer = nil;

? ? ? ? ? }

}

3>利用消息轉(zhuǎn)發(fā)機(jī)制

//利用中間鍵target 不讓timer來強(qiáng)用self

runloop -> timer -> target

示例代碼:

@interface UIViewController ()

@property (nonatomic, strong) NSTimer *timer;

@property (nonatomic, strong) id target;

@end

- (void)creatTimer{

? ? _target = [NSObject new];

? ? //我們需要使用runtime來給_target添加方法,引入頭文件 #import <objc/runtime.h>

? ? class_addMethod([_target class], @selector(fire), (IMP)fireIMP, "v@:");

? ? //timer 的target直接指向_target

? ? self.timer =? [NSTimer scheduledTimerWithTimeInterval:1.0 target:_target? selector:@selector(fire) userInfo:nil repeats:YES];

? ? [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

}

void fireIMP(id self, IMP _cmd) {

? ? NSLog(@"重復(fù)跑起來");

}

這樣只需要在VC 的dealloc方法中設(shè)置timer失效即可

- (void)dealloc{

? ? [self.timer invalidate];

? ? self.timer = nil;

}

4>中間鍵弱引用self

//runloop強(qiáng)引用timer,timer強(qiáng)引用proxy, proxy弱引用self。(弱引用不會(huì)使self的引用計(jì)數(shù)加一)

runloop -> timer -> proxy --> self(UIViewController)

用一個(gè)比NSObjec更輕量級(jí)的類NSProxy來做中間鍵

示例代碼:

創(chuàng)建NSProxy類

#import <Foundation/Foundation.h>

@interface WeakProxy : NSProxy

//使用弱引用

@property (nonatomic, weak) id target;

@end

#import "WeakProxy.h"

@implementation WeakProxy

/*

* - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel

* - (void)forwardInvocation:(NSInvocation *)invocation

* 這兩個(gè)方法必須寫

*/

//方法簽名

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{

? ? ? return [self.target methodSignatureForSelector:sel];

}

//消息轉(zhuǎn)發(fā)

- (void)forwardInvocation:(NSInvocation *)invocation{

? ? [invocation invokeWithTarget:self.target];

}

@end

回到UIViewController中

- (void)creatTimer{

? ? //應(yīng)為在上述類中我沒有寫構(gòu)造函數(shù)直接alloc。

? ? WeakProxy *proxy =? [WeakProxy alloc];

? ? //弱引用self

? ? proxy.target = self;

? ? //timer 的target直接指向proxy

? ? self.timer =? [NSTimer scheduledTimerWithTimeInterval:1.0 target:proxy? selector:@selector(fire) userInfo:nil repeats:YES];

? ? [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

}

然后只需在delloc中實(shí)現(xiàn)計(jì)時(shí)器的銷毀即可

- (void)dealloc{

? ? [self.timer invalidate];

? ? self.timer = nil;

}

俺們也是菜鳥通過看大神講解學(xué)得如果有什么問題請(qǐng)?zhí)岢?,共同討論解決。

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

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

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