iOS 推迟而不重复执行函数

如果你想推迟执行一段代码,使用dispatch_after函数可以轻易实现,但有时候我们想在它delay的期间再次delay,直到最终不再delay的时候,才真正的被调用一次。这就需要每次先将旧的事件取消,然后重新设置delay。

场景

自动消失的视图

例如当用户的某种操作点击一下屏幕就发出一条状态栏消息,消息在2秒后消失,在消失前如果用户又触发了这个条件,理应是在这次触发的2秒后消失,如果简单的使用dispatch_after函数进行延迟,结果会是在第一次点击的2秒后消失,可能你在1.5秒的时候又点击了一下,但是只过了0.5秒就消失了。

降低服务器访问次数

例如:用户修改了信息的30秒后上传至服务器。那么如果用户修改了姓名,然后30秒内又修改了头像,在修改了头像后的30秒内又修改了性别等等。我们希望的并不是每次修改就上传一次,而是在用户最后修改完所有信息之后,看起来似乎不准备继续修改了的时候,再上传至服务器。使用推迟而不重复执行的方法可以在避免多次访问服务器的同时及时地上传用户修改后的信息。

AXKitDemo

为了方便大家理解,我在Demo中做了这样一个页面,点击屏幕的时候计数器会增加,意味着收到某种需要处理的用户交互信息,2秒后作出回应(发出状态栏消息)。如果2秒内用户重复点击屏幕,那么这个回应会继续被推迟,直到某个连续的2秒没有收到任何输入,这时候才作出一次响应。

实现的方法很简单,步骤如下:

  1. 获取到函数
  2. 取消函数
  3. 重新赋值或启动

使用Objective-C

在Objective-C中,将要执行的代码保存成dispatch_block_t的静态类型变量,取出这个静态变量,然后使用dispatch_block_cancel()取消,然后重新设置它的值,然后调用dispatch_after

1
2
3
4
5
6
7
8
9
10
- (void)delayTest{
static dispatch_block_t task;
if (task) {
dispatch_block_cancel(task);
}
task = dispatch_block_create(DISPATCH_BLOCK_BARRIER, ^{
// 被推迟执行的代码
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), task);
}

也可以使用AXKit实现

也可以使用 AXKit 封装好的方法,将要执行的代码保存成ax_dispatch_operation_t的静态类型变量,使用ax_dispatch_cancel_operation()取消之前的任务,然后使用ax_dispatch_cancellable()再次赋值。

1
2
3
4
5
6
7
- (void)delayTest2{
static ax_dispatch_operation_t animationToken;
ax_dispatch_cancel_operation(animationToken);
animationToken = ax_dispatch_cancellable(duration, dispatch_get_main_queue(), ^{
// 被推迟执行的代码
});
}

使用Swift

创建静态变量static var task = DispatchWorkItem.init{},先取消task.cancel(),再赋值DispatchWorkItem,然后使用DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute: task)延迟调用。

1
2
3
4
5
6
7
8
9
internal static var task = DispatchWorkItem.init {}

internal static func hide(duration: TimeInterval) {
task.cancel()
task = DispatchWorkItem.init(block: {
// 被推迟执行的代码
})
DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute: task)
}