Подтвердить что ты не робот

Работа над отсутствием рекурсивных ограничений протокола в Swift 3

Swift 3 (Xcode 8 beta 6) в настоящее время имеет ограничение относительно "рекурсивных ограничений протокола". Существует открытая проблема здесь, и там подобные обсуждения продолжаются здесь, здесь и здесь. Тем не менее, я все еще не понимаю, как можно обойти это ограничение. Возможно ли это?

Рассмотрим простой пример представления, ссылающегося на модель представления и наоборот, и не рассматриваем типы ref/value, а также любые циклы сохранения:

protocol ViewModelType {
    associatedtype V: ViewType
    var view: V { get }
}

struct ViewModel<V: ViewType>: ViewModelType {
    var view: V
}


protocol ViewType {
    associatedtype VM: ViewModelType
    var viewModel: VM { get }
}

struct View<VM: ViewModelType>: ViewType {
    var viewModel: VM
}

С приведенным выше кодом вы столкнетесь с Type may not reference itself as a requirement, как описано в приведенных ссылках.

Тогда (наивный, как я), я думал, что смогу обойти это, сделав:

protocol _ViewModelType {}
protocol ViewModelType: _ViewModelType {
    associatedtype V: _ViewType
    var view: V { get }
}

struct ViewModel<V: ViewType>: ViewModelType {
    var view: V
}


protocol _ViewType {}
protocol ViewType: _ViewType {
    associatedtype VM: _ViewModelType
    var viewModel: VM { get }
}

struct View<VM: ViewModelType>: ViewType {
    var viewModel: VM
}

Это убивает ошибку, но в основном просто откладывает проблему. Поскольку теперь, когда мы хотим построить наши конкретные типы, мы оказываемся в бесконечном цикле специализаций:

let vm = ViewModel<View<ViewModel<View...>>>()

Я считаю, что это несколько базовое ограничение, которое я хотел бы включить в мои протоколы, но на данный момент я не вижу, как это сделать. Можно ли обойти это до тех пор, пока Swift не будет обновлен? Или мне нужно начинать вводить менее строгие протоколы, пока это не было реализовано в Swift?

Обновление 19 августа 2016 года

Попытавшись найти оптимальный способ решения этой проблемы, я считаю, что нашел приемлемое решение и требует минимальных компромиссов:

protocol ViewModelType {
    associatedtype D: Any // Compromise to avoid the circular protocol constraints.
    var delegate: D? { get set }
}

protocol ViewType {
    associatedtype VM: ViewModelType
    var viewModel: VM { get }
}

protocol ViewDelegate {
    func foo()
}


struct ViewModel: ViewModelType {
    typealias D = ViewDelegate
    var delegate: D?

    func bar() {
        delegate?.foo() // Access to delegate methods
    }
}

struct View<VM: ViewModelType>: ViewType, ViewDelegate {
    var viewModel: VM

    func foo() {
        // Preferred, but not possible: viewModel.delegate = self
    }
}


var vm = ViewModel() // Type: ViewModel
let v = View(viewModel: vm) // Type: View<ViewModel>
vm.delegate = v

Основная идея заключается в том, что объект посредника или объект-делегат вводится для обработки связи между представлением и моделью представления. Ссылка на этот делегат имеет тип Any для нарушения ограничений круглого протокола. Единственный недостаток, который я вижу, заключается в том, что делегат должен быть настроен "извне", откуда создаются объекты и не может быть установлен в реализации View. Если попытаться сделать это, появится ошибка Cannot assign value of type View<VM> to type _?.

Однако при таком подходе мы получаем правильные типы, не требуя большой специализации. Конечно, можно добавить больше протоколов, чтобы иметь еще больше абстракций, но это же решение будет применяться.

4b9b3361

Ответ 1

По каким-то причинам/языковым недостаткам вы должны явно использовать при назначении делегата в View.foo: viewModel.delegate = self as? VM.D

Но почему вы используете структуры, а не классы? Я думаю, что вам нужны классы, в частности, вы не хотите, чтобы все переменные View/ViewModel копировались во время изменения - вместо ссылки - когда вы делаете что-то вроде

var vm = ViewModel() // Type: ViewModel
let v = View(viewModel: vm) // Type: View<ViewModel> 
vm.delegate = v

Особенно

func foo() {
    viewModel.delegate = self
}

не работает, если вы не объявите View.foo как mutating, и для этого потребуется почти все (включая ViewDelegate.foo и ViewModel.bar), которые будут сделаны mutating и v = View(viewModel: vm) не могут быть константами больше.

Хотя приведенное ниже решение также будет работать с структурами, я просто изменил все на классы:

protocol ViewModelType {
    associatedtype D: Any // Compromise to avoid the circular protocol constraints.
    var delegate: D? { get set }
}

protocol ViewType {
    associatedtype VM: ViewModelType
    var viewModel: VM { get }
}

protocol ViewDelegate {
    func foo()
}


class ViewModel: ViewModelType {
    typealias D = ViewDelegate
    var delegate: D?

    func bar() {
        delegate?.foo() // Access to delegate methods
    }
}

class View<VM: ViewModelType>: ViewType, ViewDelegate {
    var viewModel: VM

    // initializer needed, because we are no struct any more
    init(viewModel vm:VM) {
        self.viewModel = vm
    }

    func foo() {
        viewModel.delegate = self as? VM.D
    }
}


var vm = ViewModel() // Type: ViewModel
let v = View(viewModel: vm) // Type: View<ViewModel>
v.foo()     // View<ViewModel>
vm.delegate // View<ViewModel>

Есть еще одна вещь: почему бы вам не назначить делегата в инициализаторе вашего класса вида, например:

// initializer needed, because we are no struct any more
init(viewModel vm:VM) {
    self.viewModel = vm
    self.viewModel.delegate = self as? VM.D
}

Затем вы можете пропустить вызов v.foo(), чтобы установить делегат.