Тип Erasure для механизма планирования задач - PullRequest
0 голосов
/ 01 июня 2018

Я пытаюсь создать легкую систему планирования задач.По сути, я могу обернуть некоторую асинхронную работу в 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()
}

Поэтому мне нравится тот факт, что я получаю строгую типизацию relatedType 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, используя только те функции, которые ему нужны.(например, он не заботится о строго типизированном ответе)

Я просмотрел обобщения, стирание типов и даже использовал иерархию классов, но не нашел достойного решения.

1 Ответ

0 голосов
/ 01 июня 2018

Я предполагаю, что цель состоит в том, чтобы 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()
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...