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 _?
.
Однако при таком подходе мы получаем правильные типы, не требуя большой специализации. Конечно, можно добавить больше протоколов, чтобы иметь еще больше абстракций, но это же решение будет применяться.