iOS:延时执行的三种方式
延时执行的三种方式:performSelectorXXX方法、GCD中延时函数、创建定时器
@interface NSObject (NSDelayedPerforming)
※延时调用在当前线程使用特定模式的方法(如果数组没有数据或者参数为nil,则不会调用selector中方法)
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
※延时调用在当前线程使用默认模式的方法
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
※取消某一个延时调用请求
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
※取消全部的延时调用请求
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
@end
-============================================================================
第二种方式:GCD当中的方法
间隔时间宏定义:NSEC纳秒、MSEC毫秒、 USEC微妙、 SEC秒、 PER每
#define NSEC_PER_SEC //每一秒有多少纳秒
#define NSEC_PER_MSEC //每一毫秒有多少纳秒
#define USEC_PER_SEC //每一秒有多少微妙(注意:在纳秒的基础上)
#define NSEC_PER_USEC //每一微秒有多少纳秒
开始时间宏定义:
#define DISPATCH_TIME_NOW //当前时间
#define DISPATCH_TIME_FOREVER //永久时间(无限循环)
相关方法:
※时间变量类型
typedef uint64_t dispatch_time_t;
※创建延时间隔时间(参数:宏定义开始时间、设置秒数*宏定义的间隔时间)
dispatch_time(dispatch_time_t when, int64_t delta);
※队列变量类型(主队列、默认全局队列、自定义队列)
dispatch_queue_t queue
※执行延时函数操作(参数:延迟时间、队列、block操作)
dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
===================================================================================
第三种方式:创建定时器
@interface NSTimer : NSObject
属性:
※设置定时器的启动时间,管理定时器的启动和停止
@property (copy) NSDate *fireDate;
※只读属性,获取时间间隔
@property (readonly) NSTimeInterval timeInterval;
※这是7.0之后的一个新特性,由于NSTimer不精确,通过它设置误差范围
@property NSTimeInterval tolerance ;
※只读属性,获取定时器是否有效
@property (readonly, getter=isValid) BOOL valid;
※参数信息
@property (readonly, retain) id userInfo;
方法:
※创建定时器的time-类方法,需要手动fire开启定时器,将执行方法封装到NSInvocation中
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
※创建定时器的time-类方法,需要手动fire开启定时器
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
※创建定时器的scheduled-类方法,不需要手动fire开启定时器,将执行方法封装到NSInvocation中
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
※创建定时器的scheduled-类方法,不需要手动fire开启定时器
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
※创建定时器的实例方法,会在指定时间开启定时器
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep;
※开启定时器
- (void)fire;
※使定时器失效,将定时器从循环池移除掉
- (void)invalidate;
@end
💗创建定时器中类方法拓展:由于用到NSInvocation对象,所以涉及到两个类NSInvocation和NSMethodSignature签名类、还有一个执行线程类NSRunLoop
❤️(1)NSInvocation : NSObject
用到的属性:
@property (assign) id target; //设置执行目标
@property SEL selector; //设置执行方法
用到的方法:
+ (NSInvocation *)invocationWithMethodSignature:(NSMethodSignature *)sig; //创建NSInvocation对象
❤️(2)NSMethodSignature : NSObject
方法:
(下面两个方法均继承自NSObject):必须是用当前要执行的方法所在的类或类对象来调用下面的类方法或实例方法创建签名对象
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; //创建方法签名对象的实例方法(当前要执行的方法所在类对象来调用)
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector; //创建方法签名对象的类方法(必须是当前要执行的方法所在的类来调用)
❤️(3)NSRunLoop:NSObject
用到的方法:
+ (NSRunLoop *)currentRunLoop; //获取当前运行线程
+ (NSRunLoop *)mainRunLoop; //获取主运行线程
- (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode; //将定时器添加到运行线程中
用到的参数模式mode:
FOUNDATION_EXPORT NSString * const NSDefaultRunLoopMode; //默认执行模式
FOUNDATION_EXPORT NSString * const NSRunLoopCommonModes //公共执行模式
注意:这五种初始化方法的异同:
1、参数repeats是指定是否循环执行,YES将循环,NO将只执行一次。
2、timerWithTimeInterval这两个类方法创建出来的对象如果不用 addTimer: forMode方法手动加入主循环池中,将不会循环执行。如果将定时器加到了主循环池中,则不需要手动调用fire启动定时器;相反,如果没有将定时器加到主循环池中,则需要手动调用fire,此时定时器会启动执行一次。
3、scheduledTimerWithTimeInterval这两个方法不需要手动调用fire,会自动执行,并且自动加入主循环池。
4、init方法需要手动加入循环池,它会在设定的启动时间启动。
例如加入循环池:
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
启动定时器:
[timer fire];
=================================================================================
具体举例如下:
第一种方式:用NSObject的performSelectorXXX方法进行延迟调用。
1.创建延时请求
[self performSelector:@selector(printString:) withObject:@"hello world" afterDelay:3.0];
2.创建被调用的函数
-(void)printString:(NSString *)str
{
NSLog(@"%@",str);
}
运行结果如下:
2015-10-07 20:27:40.938 03-performSelector-delay[2968:222584] hello world
第二种方式:用GCD中的dispatch_after方法进行延迟调用。
1.创建延时请求
//创建延迟时间,从当前时间开始,3秒后执行。 3秒需要转化为纳秒,因为该函数是以纳秒为基础进行的
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, 3.0*NSEC_PER_SEC);
//执行延迟函数 dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self printString:@"hello world"]; });
2.创建被调用的函数
-(void)printString:(NSString *)str
{
NSLog(@"%@",str);
}
运行结果如下:
2015-10-07 20:27:40.940 03-GCD-delay[2968:222584] hello world
第三种方式:使用定时器NSTimer进行延迟调用
3.1采用第一个类方法,循环延迟调用(此方法不需要手动调用fire启动定时器,也不需要将定时器加入到循环池才能重复执行)
//类方法创建定时器
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(printString:)userInfo:nil repeats:YES];
//执行的方法
-(void)printString:(NSTimer *) timer
{
NSLog(@"hello world");
}
运行结果:我只粘贴出执行4次的结果,,,,
2015-10-07 20:27:40.938 03-延迟调用-delay[2968:222584] hello world
2015-10-07 20:27:40.940 03-延迟调用-delay[2968:222584] hello world
2015-10-07 20:27:40.940 03-延迟调用-delay[2968:222584] hello world
2015-10-07 20:27:41.226 03-延迟调用-delay[2968:222719] hello world
3.2采用第二个类方法:(分循环执行和只执行一次的情况,演示如下)
《1》循环延迟调用(把定时器加入到循环池中,此时不需要手动调用fire启动定时器,定时器能重复执行)
//类方法创建定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:3.0 target:self selector:@selector(printString:) userInfo:nil repeats:YES];
//将定时器加到循环池中
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
//执行的方法
-(void)printString:(NSTimer *) timer
{
NSLog(@"hello world");
}
运行结果:我只粘贴出执行4次的结果,,,,
2015-10-07 20:27:40.938 03-延迟调用2-delay[2968:222584] hello world
2015-10-07 20:27:40.940 03-延迟调用2-delay[2968:222584] hello world
2015-10-07 20:27:40.940 03-延迟调用2-delay[2968:222584] hello world
2015-10-07 20:27:41.226 03-延迟调用2-delay[2968:222719] hello world
《2》只调用一次,没有把定时器加入到循环池,需要手动调用fire来启动定时器
//类方法创建定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:3.0 target:self selector:@selector(printString:) userInfo:nil repeats:NO]; //启动定时器 [timer fire]; //定时器失效 //[timer invalidate];
//执行的方法
-(void)printString:(NSTimer *) timer
{
NSLog(@"hello world");
}
运行结果如下:只有一个输出
2015-10-07 21:12:54.031 03-延迟执行3-delay[3176:239321] hello world
3.3采用第三个类方法循环调用延迟函数:需要创建签名对象和NSInvocation对象,来设置目标和执行方法,此方法不需要手动启动定时器和加入循环池
//创建签名类标识对象(用当前要执行的方法和它所在的类做签名)
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:@selector(printString)];
//创建NSInvocation实例
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//设置目标和执行方法
[invocation setTarget:self];
[invocation setSelector:@selector(printString)];
//类方法创建定时器
[NSTimer scheduledTimerWithTimeInterval:3.0 invocation:invocation repeats:YES];
//执行方法
-(void)printString
{
NSLog(@"hello world");
}
运行结果:我只粘贴出执行4次的结果,,,,
2015-10-07 21:35:15.429 03-延迟执行4-delay[3258:247360] hello world
2015-10-07 21:35:18.428 03-延迟执行4-delay[3258:247360] hello world
2015-10-07 21:35:21.429 03-延迟执行4-delay[3258:247360] hello world
2015-10-07 21:35:24.428 03-延迟执行4-delay[3258:247360] hello world
3.4采用实例方法进行循环延迟调用:循环延迟调用(把定时器手动加入到循环池中,定时器能重复执行)
//创建首次启动时间
NSDate *date = [NSDate distantPast]; //过去某个时间(可以自定义)
//创建定时器
NSTimer *timer = [[NSTimer alloc]initWithFireDate:date interval:3.0 target:self selector:@selector(printString) userInfo:nil repeats:YES];
//把定时器加到循环池
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
//执行的方法
-(void)printString
{
NSLog(@"hello world");
}
运行结果:我只粘贴出执行4次的结果,,,,
2015-10-07 22:22:35.373 03-延迟执行5-delay[3429:266949] hello world
2015-10-07 22:22:38.326 03-延迟执行5-delay[3429:266949] hello world
2015-10-07 22:22:41.326 03-延迟执行5-delay[3429:266949] hello world
2015-10-07 22:22:44.326 03-延迟执行5-delay[3429:266949] hello world
还有其他没演示的方法,差不多一样,就不一一演示了。