iOS 延迟执行和取消

252 阅读2分钟

一、performSelector

连续收到很多条新消息时怎么处理:存数据库+刷新界面?

在刚打开App时或者在前台使用时突然连续不断收到很多条新消息时,需要加一个延时2秒判断逻辑,如果3秒内没有收到新消息,则将3秒内收到的数据存入数据库并刷新界面,如果3秒内收到了新消息就取消当前的延时方法,并递归调用启动新的延时方法。

延时方法:
[self performSelector:@selector(delay) withObject:nil afterDelay:3.0];
取消延时方法:
方法一:

这里需要注意参数需要保持一致,否则取消失败。

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delay) object:nil];
方法二:

可以取消所有的延迟调用

[NSObject cancelPreviousPerformRequestsWithTarget:self];
遇到的问题:

cancelPreviousPerformRequestsWithTarget 不管用,计时依然存在,这是因为 performSelectorcancelPreviousPerformRequestsWithTarget 必须在同一个线程中执行才行

二、NSTimer

延时方法:
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delay) userInfo:nil repeats:NO];
取消延时方法:
[self.timer invalidate];
self.timer = nil;

三、dispatch_after

延时方法:

dispatch_after 能让我们添加进队列的任务延迟执行,该函数并不是在指定时间后执行处理,而只是在指定时间追加到自己需要的队列中去执行。

方法定义:
/*
dispatch_time_t:时间参数
dispatch_queue_t:队列
dispatch_block_t:写入需要执行的操作
*/
dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
        dispatch_block_t block);
        
延迟执行:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

//表示延迟2秒后执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^{
        /*你的操作*/
    });

当然如果是主线程择将 queue 换成 dispatch_get_main_queue()

取消延时方法:

通过再封装达到取消dispatch_after代码块执行

typedef void(^BranDelayedBlockHandle) (BOOL cancel);
@property (nonatomic, assign) BranDelayedBlockHandle delayedBlockHandle;

//用再封装的延迟执行方法
_delayedBlockHandle = perform_block_after_delay(3.0, ^{
   NSLog(@"buttonAction_____3s后执行dispatch_after代码块中的内容");
});
//取消
cancel_delayed_block(_delayedBlockHandle);

static BranDelayedBlockHandle perform_block_after_delay(CGFloat seconds, dispatch_block_t block) {
    if (block == nil) {
        return nil;
    }
      
    __block dispatch_block_t blockToExecute = [block copy];
    __block BranDelayedBlockHandle delayHandleCopy = nil;
      
    BranDelayedBlockHandle delayHandle = ^(BOOL cancel) {
        if (!cancel && blockToExecute) {
            blockToExecute();
        }
          
        // Once the handle block is executed, canceled or not, we free blockToExecute and the handle.
        // Doing this here means that if the block is canceled, we aren't holding onto retained objects for any longer than necessary.
#if !__has_feature(objc_arc)
        [blockToExecute release];
        [delayHandleCopy release];
#endif
          
        blockToExecute = nil;
        delayHandleCopy = nil;
    };
    // delayHandle also needs to be moved to the heap.
    delayHandleCopy = [delayHandle copy];
      
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if (nil != delayHandleCopy) {
            delayHandleCopy(NO);
        }
    });
      
    return delayHandleCopy;
}

static void cancel_delayed_block(BranDelayedBlockHandle delayedHandle) {
    if (nil == delayedHandle) {
        return;
    }
      
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        delayedHandle(YES);
    });
}

调用:

@property (nonatomic, assign) BranDelayedBlockHandle delayedBlockHandle;
 
// 执行
_delayedBlockHandle = perform_block_after_delay(秒数, ^{  
	 // 执行内容  
 });  
 
// 取消
cancel_delayed_block(_delayedBlockHandle);