一、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í)岢?,共同討論解決。