关于NSTimer

关于NSTimer

在工作中经常会做一些延时任务,或者周期性任务,有时候也需要对取消延时任务操作。

延时任务一般有三种
  • NSObject的
 -(void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
  • 使用NSTimer的一些函数
  +(NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
  • 使用GCD的一些函数

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{});


> 如果这个任务不需要撤销,使用GCD是最好的选择
如果需要有撤销操作,就不能使用GCD,使用NSObject的函数或者使用NSTimer需要注意一些细节

* 必须有一个活跃的RunLoop, performSelector和NSTimer的一些函数都是基于RunLoop的,主线程的RunLopp是默认启动的,如果放在子线程创建必须手动启动子线程的RunLoop。
* NSTimer的创建和撤销需要放在同一个线程操作,performSelector的创建与撤销必须在同一个线程操作。
* 有内存泄露的潜在风险,scheduledTimerWithTimeInterval函数会对target进行强引用,而NSTimer会被当前的runloop强引用,只有当调用NSTimer的invalidate才能解除掉这个强引用,performSelector也同样会对target进行强引用,必须手动取消才可以解除这个强引用,当我们创建一个NSTimer的时候,我们在当前对象的dealloc函数中invalidate这个timer,看似合理,但是dealloc永远不会被调用,造成了内存泄露

> 看下苹果对NSTimer的invalidate函数介绍
> Discussion
>
This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes its strong reference to the timer, either just before the invalidate method returns or at some later point.
>
If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.
>
Special Considerations
>
You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

只有这个函数才能从runloop中删除这个定时器。

>
所以我们使用NSTimer会考虑timer何时释放的问题,我们一般的做法是在页面显示的时候启动这个定时器,页面消失的时候停止这个定时器

但是有的时候有需求,需要定时器不要停止,我们从当前页面push到另外一个页面的时候 定时器不停止。如果停止定时器放在viewWillDisappear函数中时,push到其他页面定时器一样会停止。
>
保证Timer一定会被停止,可以手动停止,就是调用invalidate,可以在页面退出的时候手动的去invalidate,(在back的时候,等等)。
只前遇到这种需求,对Timer做了一个简单的封装,先看代码

BFSTimerextension.h

import <Foundation/Foundation.h>

@protocol BFSTimerextensionDelegate <NSObject>
/**
定时器调用代理函数
/
-(void)timerfucntionCall;
@end
@interface BFSTimerextension : NSObject
@property(nonatomic,weak) id<BFSTimerextensionDelegate> delegate;
/
*
启动一个定时器
@param timeInterval 间隔时间
@param repeats 是否重复
/
-(void)startTimer:(NSTimeInterval)timeInterval repeats:(BOOL)repeats;
/
*
停止这个定时器
*/
-(void)stopTimer;
@end


   BFSTimerextension.m
   

import "BFSTimerextension.h"

@interface BFSTimerextension()
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation BFSTimerextension
-(void)dealloc
{
NSLog(@"被释放");
}

-(void)startTimer:(NSTimeInterval)timeInterval repeats:(BOOL)repeats
{
if(!_timer)
{
_timer = [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(timerSelectCall) userInfo:nil repeats:repeats];
}
}

-(void)timerSelectCall
{
if(self.delegate && [self.delegate respondsToSelector:@selector(timerfucntionCall)])
{
[self.delegate timerfucntionCall];
}else{
[self.timer invalidate];
self.timer = nil;
}
}

-(void)stopTimer
{
if(self.timer)
{
[self.timer invalidate];
self.timer = nil;
}
}
@end



使用BFSTimerextension创建一个Timer  timer的tagret是BFSTimerextension,如果BFSTimerextension的delegate被释放,timer直接释放掉,我们不用在考虑timer什么时机停止的问题,

注:文中若有错误,请指出,谢谢
代码地址请点击[这里](https://github.com/CharType/Timerextension)





 
 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • NSTimer使用方式 常用的创建方式有 1+ (NSTimer *)timerWithTimeInterval:...
    开发全靠xib阅读 256评论 0 0
  • 想了解NSTimer的坑我们应该先熟悉下NSTimer的原理也就是他是怎么实现定时器的 runloop处理三种事件...
    RFeng阅读 770评论 0 2
  • NSTimer 一个计时器,不算常用但也算基础。我想每个开发iOS都应该知道,所以我一般会把他当作一个面试题。可以...
    jing091111阅读 362评论 5 2
  • 面试中,经常会问道 NSTimer 循环引用的问题。闲话少叙。下面来讲讲 NSTimer 为什么会造成循环引用? ...
    人话博客阅读 1,474评论 0 53
  • 看一本说吃说喝的书,忽然想起了羊汤。曾经有几年,午饭还在单位吃时,每个冬天,都要去喝几回羊汤。 那时,农场那一带除...
    铅笔芒种阅读 345评论 0 1