iOS中定时器NSTimer的一些用法总结

时间:2021-08-04 03:23:17

NSTimer在IOS开发中会经常用到,尤其是小型游戏,然而对于初学者时常会注意不到其中的内存释放问题,将其基本用法总结如下:

一、初始化方法:有五种初始化方法,分别是

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

?
123456789101112 - (void)viewDidLoad {    [super viewDidLoad];    //初始化一个Invocation对象    NSInvocation * invo = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(init)]];    [invo setTarget:self];    [invo setSelector:@selector(myLog)];    NSTimer * timer = [NSTimer timerWithTimeInterval:1 invocation:invo repeats:YES];    //加入主循环池中    [[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];    //开始循环    [timer fire];}

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

?
1   NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 invocation:invo repeats:YES];

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

?
1 NSTimer * timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(myLog) userInfo:nil repeats:NO]

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

?
1 NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myLog:) userInfo:@"123" repeats:YES]

- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep 

?
12  NSTimer * timer = [[NSTimer alloc]initWithFireDate:[NSDate distantPast] interval:1 target:self selector:@selector(myLog:) userInfo:nil repeats:YES];    [[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];

注意:这五种初始化方法的异同:

        1、参数repeats是指定是否循环执行,YES将循环,NO将只执行一次。

        2、timerWithTimeInterval这两个类方法创建出来的对象如果不用 addTimer: forMode方法手动加入主循环池中,将不会循环执行。并且如果不手动调用fair,则定时器不会启动。

        3、scheduledTimerWithTimeInterval这两个方法不需要手动调用fair,会自动执行,并且自动加入主循环池。

        4、init方法需要手动加入循环池,它会在设定的启动时间启动。

二、成员变量

@property (copy) NSDate *fireDate;

这是设置定时器的启动时间,常用来管理定时器的启动与停止

?
1234     //启动定时器    timer.fireDate = [NSDate distantPast];    //停止定时器    timer.fireDate = [NSDate distantFuture];

@property (readonly) NSTimeInterval timeInterval;

这个是一个只读属性,获取定时器调用间隔时间。

@property NSTimeInterval tolerance;

这是7.0之后新增的一个属性,因为NSTimer并不完全精准,通过这个值设置误差范围。

@property (readonly, getter=isValid) BOOL valid;

获取定时器是否有效

@property (readonly, retain) id userInfo;

获取参数信息

三、关于内存释放

如果我们启动了一个定时器,在某个界面释放前,将这个定时器停止,甚至置为nil,都不能是这个界面释放,原因是系统的循环池中还保有这个对象。所以我们需要这样做:

?
123456789101112131415161718 -(void)dealloc{    NSLog(@"dealloc:%@",[self class]);}- (void)viewDidLoad {    [super viewDidLoad];    timer= [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myLog:) userInfo:nil repeats:YES];    UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];    btn.backgroundColor=[UIColor redColor];    [btn addTarget:self action:@selector(btn) forControlEvents:UIControlEventTouchUpInside];    [self.view addSubview:btn];}-(void)btn{    if (timer.isValid) {        [timer invalidate];    }    timer=nil;    [self dismissViewControllerAnimated:YES completion:nil];}

在官方文档中我们可以看到 [timer invalidate]是唯一的方法将定时器从循环池中移除。



OS程序进入后台后十分钟之内就会被系统kill掉,怎么解决呢?我想要程序进入后台后仍然运行计时功能,否则就无法达到考试的目的,之后在网上查阅了相关资料最后终于找到答案,其精髓就是:利用苹果给出的三种类型的程序可以保持在后台运行:音频播放类,位置更新类,另外一个记不太清楚了,我利用了苹果给出的音频播放类的这个“特权”来满足我程序上的要求,详细步骤如下:

1、步骤一:在Info.plist中,添加"Required background modes"键,value为:App plays audio

iOS中定时器NSTimer的一些用法总结

步骤二:

iOS中定时器NSTimer的一些用法总结
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window
= [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
NSError *setCategoryErr = nil;
NSError
*activationErr = nil;
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error:
&setCategoryErr];
[[AVAudioSession sharedInstance]
setActive: YES
error:
&activationErr];
self.window.backgroundColor
= [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
iOS中定时器NSTimer的一些用法总结

步骤三:将以下代码添加到appDelegate文件中的- (void)applicationDidEnterBackground:(UIApplication *)application函数,也可添加到在具体类中注册的应用进入后台后的通知方法

iOS中定时器NSTimer的一些用法总结
- (void)applicationDidEnterBackground:(UIApplication *)application{

UIApplication
* app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask;
bgTask
= [app beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(),
^{
if (bgTask != UIBackgroundTaskInvalid)
{
bgTask
= UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0), ^{
dispatch_async(dispatch_get_main_queue(),
^{
if (bgTask != UIBackgroundTaskInvalid)
{
bgTask
= UIBackgroundTaskInvalid;
}
});
});

}

问题解决之后遇到的一个新问题,我的页面上有一个UIScrollView和一个定时器用来记录当前考试模式下的剩余时间,问题出现了:当我滑动滚动试图时,定时器的方法便不在运行(即被UI主线程阻塞)。google一下找到了解决办法:将定时器放在非主线程中执行将更新UI的操作放到主线程,这样UI主线程和定时器就能互不干扰的相互工作了,以下是主要代码:

iOS中定时器NSTimer的一些用法总结
  1 #import "CountdownTool.h"
2
3 @interface CountdownTool()
4 {
5 UILabel *_lblShow;
6 NSTimer *_timer;
7 }
8 @property (nonatomic, assign) NSInteger hour;
9 @property (nonatomic, assign) NSInteger minute;
10 @property (nonatomic, assign) NSInteger second;
11 @property (nonatomic, copy) NSString *strHour;
12 @property (nonatomic, copy) NSString *strMinute;
13 @property (nonatomic, copy) NSString *strSecond;
14 @property (nonatomic, assign) NSInteger totalSeconds;
15 @end
16 @implementation CountdownTool
17 @synthesize hour = _hour;
18 @synthesize minute = _minute;
19 @synthesize second = _second;
20 @synthesize totalSeconds = _totalSeconds;
21
22 - (void)dealloc
23 {
24 [_lblShow release];
25 [_strHour release];
26 [_strMinute release];
27 [_strSecond release];
28 [super dealloc];
29 }
30
31 - (id)initWithFrame:(CGRect)frame
32 {
33 self = [super initWithFrame:frame];
34 if (self) {
35 _lblShow = [[UILabel alloc] initWithFrame:self.bounds];
36 _lblShow.backgroundColor = [UIColor clearColor];
37 _lblShow.font = [UIFont systemFontOfSize:15];
38 _lblShow.textColor = [UIColor yellowColor];
39 _lblShow.textAlignment = NSTextAlignmentCenter;
40 _lblShow.numberOfLines = 1;
41 [self addSubview:_lblShow];
42 }
43 return self;
44 }
45
46 - (id)initWithFrame:(CGRect)frame andMinutesNum:(NSInteger)minute
47 {
48 if (self = [self initWithFrame:frame]) {
49 self.totalSeconds = minute * 60;
50 //多线程启动定时器
51 [NSThread detachNewThreadSelector:@selector(startTimer) toTarget:self withObject:nil];
52 }
53 return self;
54 }
55 - (void)startTimer
56 {
57 _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerFire) userInfo:nil repeats:YES];
58 [[NSRunLoop currentRunLoop] run];
59 }
60 - (void)handleWithTotalSeconds
61 {
62 self.hour = _totalSeconds/3600;
63 self.minute = _totalSeconds%3600/60;
64 self.second = _totalSeconds%3600%60;
65 if (_hour <= 0) {
66 _lblShow.text = [NSString stringWithFormat:@"%@:%@",_strMinute,_strSecond];
67 }else{
68 _lblShow.text = [NSString stringWithFormat:@"%@:%@:%@",_strHour,_strMinute,_strSecond];
69 }
70 }
71 - (void)setHour:(NSInteger)hour
72 {
73 _hour = hour;
74 if (_hour < 10) {
75 self.strHour = [NSString stringWithFormat:@"0%d",_hour];
76 }else{
77 self.strHour = [NSString stringWithFormat:@"%d",_hour];
78 }
79 }
80 - (void)setMinute:(NSInteger)minute
81 {
82 _minute = minute;
83 if (_minute < 10) {
84 self.strMinute = [NSString stringWithFormat:@"0%d",_minute];
85 }else{
86 self.strMinute = [NSString stringWithFormat:@"%d",_minute];
87 }
88 }
89 - (void)setSecond:(NSInteger)second
90 {
91 _second = second;
92 if (_second < 10) {
93 self.strSecond = [NSString stringWithFormat:@"0%d",_second];
94 }else{
95 self.strSecond = [NSString stringWithFormat:@"%d",_second];
96 }
97 }
98 - (void)setTotalSeconds:(NSInteger)totalSeconds
99 {
100 _totalSeconds = totalSeconds;
101 [self performSelectorOnMainThread:@selector(handleWithTotalSeconds) withObject:nil waitUntilDone:YES];
102 }
103 - (void)timerFire
104 {
105 if (_totalSeconds == 0) {
106 [_timer invalidate];
107 return;
108 }
109 self.totalSeconds -= 1;
110 }
111 @end