8.0 KiB
Schedule
Schedule 是一个轻量级的调度框架,它能让你用难以置信的友好语法执行定时任务。
功能
- 多种调度规则
- 暂停、继续、取消
- 重置调度规则
- 基于 tag 的任务管理
- 添加、移除子动作
- 自然语言解析
- 原子操作
- 对生命周期的完全控制
- 95%+ 测试覆盖
- 完善的文档(所有 public 类型和方法)
- 支持 Linux(通过 Ubuntu 16.04 测试)
为什么你该用 Schedule
功能 | Timer | DispatchSourceTimer | Schedule |
---|---|---|---|
⏰ 基于时间间隔调度 | ✓ | ✓ | ✓ |
📆 基于日期调度 | ✓ | ✓ | |
🌈 自定义规则调度 | ✓ | ||
🚦 暂停、继续、取消 | ✓ | ✓ | |
🎡 重置规则 | ✓ | ✓ | |
🏷 基于 tag 的任务管理 | ✓ | ||
🍰 添加、移除子动作 | ✓ | ||
📝 自然语言解析 | ✓ | ||
🚔 原子操作 | ✓ | ||
🕕 生命周期绑定 | ✓ | ||
🚀 实时观察时间线 | ✓ | ||
🏌 寿命设置 | ✓ |
用法
一瞥
调度一个定时任务从未如此简单直观,你要做的只有:
// 1. 定义你的计划:
let plan = Plan.after(3.seconds)
// 2. 执行你的任务:
let task = plan.do {
print("3 seconds passed!")
}
规则
基于时间间隔调度
Schedule 使用自定义的 Interval
类型来配置定时任务,你不必担心对内置类型的扩展会污染你的命名空间。流畅的构造方法让配置像一场舒服的对话:
let t1 = Plan.every(1.second).do { }
let t2 = Plan.after(1.hour, repeating: 1.minute).do { }
let t3 = Plan.of(1.second, 2.minutes, 3.hours).do { }
基于日期调度
配置基于日期的调度同样如此,Schedule 定义了所有常用的日期类型,尽力让你的书写直观、流畅:
let t1 = Plan.at(when).do { }
let t2 = Plan.every(.monday, .tuesday).at("9:00:00").do { }
let t3 = Plan.every(.september(30)).at(10, 30).do { }
let t4 = Plan.every("one month and ten days").do { }
let t5 = Plan.of(date0, date1, date2).do { }
自然语言解析
除此之外,Schedule 还支持基础的自然语言解析,这大大增强了你的代码的可读性:
let t1 = Plan.every("one hour and ten minutes").do { }
let t2 = Plan.every("1 hour, 5 minutes and 10 seconds").do { }
let t3 = Plan.every(.firday).at("9:00 pm").do { }
Period.registerQuantifier("many", for: 100 * 1000)
let t4 = Plan.every("many days").do { }
自定义规则调度
Schedule 还提供了几个简单的集合操作符,这意味着你可以使用它们定制属于你的强大规则:
/// Concat
let p0 = Plan.at(birthdate)
let p1 = Plan.every(1.year)
let birthday = p0.concat.p1
let t1 = birthday.do {
print("Happy birthday")
}
/// Merge
let p3 = Plan.every(.january(1)).at("8:00")
let p4 = Plan.every(.october(1)).at("9:00 AM")
let holiday = p3.merge(p4)
let t2 = holiday.do {
print("Happy holiday")
}
/// First
let p5 = Plan.after(5.seconds).concat(Schedule.every(1.day))
let p6 = s5.first(10)
/// Until
let p7 = P.every(.monday).at(11, 12)
let p8 = p7.until(date)
创建
寄生
Schedule 提供了一种寄生机制,它让你可以以一种更优雅的方式处理 task 的生命周期:
Plan.every(1.second).do(host: self) {
// task 会在 host 被 deallocated 后自动被 cancel
// 这在你想要把一个 task 的生命周期绑定到控制器上时非常有用
}
RunLoop
Task 默认会在当前线程上执行,它的实现依赖于 RunLoop,所以你需要保证当前线程有一个可用的 RunLoop。如果 task 的创建在子线程上,你可能需要执行 RunLoop.current.run()
。默认情况下, task 会被添加到 .common
mode 上,你可以在创建 task 时指定其它 mode:
let task = Plan.every(1.second).do(mode: .default) {
print("on default mode...")
}
DispatchQueue
你也可以使用 queue 来指定 task 会被派发到哪个 DispatchQueue 上,这时,task 的执行不再依赖于 RunLoop,意味着你可以放心地子线程上使用:
let task = Plan.every(1.second).do(queue: .global()) {
print("On a globle queue")
}
管理
你可以 suspend,resume,cancel 一个 task。
let task = Plan.every(1.minute).do { }
// 会增加 task 的暂停计数
task.suspend()
// 会减少 task 的暂停计数,不过不用担心过度减少,
// 我会帮你处理好这些~
task.resume()
// 取消任务,这会把任务从内部持有者那儿移除
// 也就是说,会减少 task 的引用计数
// 如果没有其它持有者的话,这个任务就会被释放
task.cancel()
子动作
你可以添加更多的 action 到一个 task 上去,并在任意时刻移除它们:
let dailyTask = Plan.every(1.day)
dailyTask.addAction {
print("open eyes")
}
dailyTask.addAction {
print("get up")
}
let key = dailyTask.addAction {
print("take a shower")
}
dailyTask.removeAction(byKey: key)
TaskCenter 和 Tag
Task 默认会被添加到 TaskCenter.default 上,你可以使用 tag 配合 taskCenter 来组织 tasks:
let plan = Plan.every(1.day)
let task0 = plan.do(queue: myTaskQueue) { }
let task1 = plan.do(queue: myTaskQueue) { }
TaskCenter.default.addTags(["database", "log"], to: task1)
TaskCenter.default.removeTag("log", from: task1)
TaskCenter.default.suspend(byTag: "log")
TaskCenter.default.resume(byTag: "log")
TaskCenter.default.cancel(byTag: "log")
TaskCenter.default.clear()
let myCenter = TaskCenter()
myCenter.add(task0) // will remove task0 from default center.
Timeline
你可以实时地观察 task 的当前时间线:
let timeline = task.timeline
print(timeline.initialization)
print(timeline.firstExecution)
print(timeline.lastExecution)
print(timeline.estimatedNextExecution)
Lifetime
也可以精确地设置 task 的寿命:
// 会再 10 小时后取消该 task
task.setLifetime(10.hours)
// 会给该 task 的寿命增加 1 小时
task.addLifetime(1.hour)
task.restOfLifetime == 11.hours
支持
- iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+
- Linux(Tested on Ubuntu 16.04)
安装
CocoaPods
# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'Schedule', '~> 1.0'
end
Carthage
github "jianstm/Schedule" ~> 1.0
Swift Package Manager
dependencies: [
.package(url: "https://github.com/jianstm/Schedule", .upToNextMajor("1.0.0"))
]
致谢
项目灵感来自于 Dan Bader 的 schedule!
贡献
喜欢 Schedule 吗?谢谢!与此同时我需要你的帮助:
找 Bugs
Schedule 还是一个非常年轻的项目,很难说项目离 bug free 还有多远。如果你能帮 Schedule 找到或者解决还没被发现的 bug 的话,我将感激不尽!
新功能
对项目有什么新的想法吗?尽管在 issue 里分享出来,或者你也可以直接提交你的 Pull Request!
改善文档
对 README 或者文档注释的改善建议在任何时候都非常欢迎,无论是错别字还是纠正我的蹩脚英文。对使用者来说,有时文档要比具体的代码实现要重要得多。
分享
无疑,用的人越多,项目就会变得越健壮,所以,star!fork!然后告诉你的朋友们吧!