延時執行的三種方式: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
還有其他沒演示的方法,差不多一樣,就不一一演示了。