我正在尝试构建一个轻量级的任务调度系统。基本上我可以在Task
中包含一些异步工作,并用TaskRunner
发送它。目前,跑步者非常简单,只需在start
上调用Task
并保留它,以便在它运行时不会取消分配。任务看起来像这样:
enum TaskNotification<T> {
case start
case cancel
case complete(T)
case progress(TaskProgress)
}
typealias TaskNotificationHandler<TaskResponseType> = (TaskNotification<TaskResponseType>) -> Void
protocol Task: AnyObject {
associatedtype Response
var onChange: TaskNotificationHandler<Response> { get set }
func start()
func cancel()
}
所以我喜欢这样一个事实:我得到了LinkedType Response
的强类型。但是,当需要构建TaskRunner
时,我会得到经典的"contains Self or AssociatedType requirements"
编译器错误,因此我使用T: Task
或创建一个名为AnyTask
的类型擦除包装。
class TaskRunner {
var currentTask: AnyTask<???>
func run<T: Task>(task: T) {
let boxed = AnyTask<???>(with: task)
currentTask = task
task.start()
}
}
但即使使用类型擦除(仍然不清楚什么是“擦除”和“不擦除”),它仍然需要我决定通用参数<???>
的单个具体类型。
最后我想要的是能够使用协议的关联类型中的强类型构建Task
的具体实现,但允许TaskRunner
管理任意Task
只使用它关心的功能。 (例如,它不关心强类型响应)
我已经查看了泛型,类型擦除,甚至使用了类层次结构,但还没有找到合适的解决方案。
答案 0 :(得分:1)
我假设TaskRunner
的目标是跟踪所有正在运行的任务,直到他们发出.complete
或.cancel
。如果这是目标,那么基本块(() -> Void
)就是你需要的所有类型擦除。
class TaskRunner {
// An increasing identifier just to keep track of things that aren't equatable
var nextTaskId = 0
var inProgressTasks: [Int: () -> Void] = [:]
func run<T: Task>(task: T) {
// Get an id
let taskId = nextTaskId
nextTaskId += 1
// This allows you easily write a `TaskRunner.cancelAll()` method, so
// it's useful. But it's real point is to retain `task` until it
// completes, while type-erasing it so it can be stored in inProgressTasks
let cancel: () -> Void = {
task.cancel()
}
// Extend the onChange handler to remove this task when it completes.
// This intentionally retains self so the TaskRunner cannot go away
// until all Tasks complete
let oldChange = task.onChange
task.onChange = { [self] notification in
oldChange(notification)
switch notification {
case .complete, .cancel: self.inProgressTasks.removeValue(forKey: taskId)
case .start, .progress: break
}
}
// Retain the task via its canceller
inProgressTasks[taskId] = cancel
// Run it!
task.start()
}
}