Связанный протокол в Swift - PullRequest
0 голосов
/ 06 мая 2019

Я пытаюсь абстрагировать представления, настроенные из модели представления.До сих пор я использовал связанные типы:

public protocol ViewModelProtocol: Equatable {}

public protocol ModeledView: class {
    /// The type of the view model
    associatedtype ViewModel: ViewModelProtocol

    var viewModel: ViewModel? { get }

    /// Sets the view model. A nil value describes a default state.
    func set(newViewModel: ViewModel?)
}

Которые я могу использовать как:

struct MyViewModel: ViewModelProtocol {
    let foo: String

    static public func == (lhs: MyViewModel, rhs: MyViewModel) -> Bool {
        return lhs.foo == rhs.foo
    }
}

class MyView: UIView, ModeledView {
    typealias ViewModel = MyViewModel

    private(set) var viewModel: MyViewModel?

    public func set(newViewModel: MyViewModel?) {
        print(newViewModel?.foo)
    }
}

Однако я бы хотел указать протокол для моделей представления, а неконкретизированный тип.Причина в том, что одна структура / класс может соответствовать нескольким таким протоколам модели представления.Я не хочу ни преобразовывать этот объект в другой тип, просто чтобы передать его представлению, либо иметь представление, имеющее связанный тип с большим количеством требований, чем ему нужно.Поэтому я думаю, что хотел бы сделать что-то вроде:

protocol MyViewModelProtocol: ViewModelProtocol {
    var foo: String { get }
}

class MyView: UIView, ModeledView {
    typealias ViewModel = MyViewModelProtocol

    private(set) var viewModel: MyViewModelProtocol?

    public func set(newViewModel: MyViewModelProtocol?) {
        print(newViewModel?.foo)
    }
}

struct DataModel: MyViewModelProtocol {
    let foo: String

    let bar: String

    static public func == (lhs: MyViewModel, rhs: MyViewModel) -> Bool {
        return lhs.foo == rhs.foo && lhs.bar == rhs.bar
    }
}

let dataModel = DataModel(foo: "foo", bar: "bar")
let view = MyView()
view.set(newViewModel: dataModel)

Это не работает.Компилятор говорит, что MyView не соответствует протоколу ModeledView, и намекает, что

Possibly intended match 'MyView.ViewModel' (aka 'MyViewModelProtocol') does not conform to 'ViewModelProtocol'

Я действительно не понимаю, что беспокоит компилятор, поскольку MyViewModelProtocol определяется как расширение ViewModelProtocol

1 Ответ

0 голосов
/ 06 мая 2019

MyViewModelProtocol определяется как расширение ViewModelProtocol

Правильно.MyViewModelProtocol extends ViewModelProtocol.Это не не соответствует ViewModelProtocol.Это классический случай «протоколы не соответствуют самим себе».Для вашего связанного типа требуется, чтобы ViewModel был конкретным типом, соответствующим ViewModelProtocol, а MyViewModelProtocol не является конкретным типом и ни к чему не соответствует (протоколы не соответствуют протоколам).

Способ решения этой проблемыдолжен начинаться с вызывающего кода , а затем создавать протоколы, поддерживающие то, как вы хотите, чтобы вызывающий код выглядел.В приведенном вами коде правильное решение - полностью избавиться от ModeledView.Это ничего не делает;ничто не полагается на это, и это не обеспечивает никаких расширений.Я предполагаю, что в вашем "реальном" коде что-то действительно полагается на это.Это ключевой кусок кода, на котором нужно сосредоточиться.Лучший способ добиться этого - написать несколько конкретных примеров типов соответствия, которые вы хотели бы существовать.(Если у вас возникли проблемы при написании нескольких конкретных реализаций, то пересмотрите, есть ли что-то, что нужно абстрагировать.)


Просто для более подробного объяснения, почему это не будет работать в вашем конкретном случае (это не будет работатьв более общих случаях также, но ваш случай накладывает дополнительные ограничения, которые важны).Вы потребовали, чтобы типы, которые соответствуют ViewModelProtocol, также соответствовали Equatable.Это означает, что они должны реализовать:

static public func == (lhs: Self, rhs: Self) -> Bool {

Это не означает, что некоторый тип, соответствующий ViewModelProtocol, может быть эквивалентен другому типу, соответствующему ViewModelProtocol.Это означает Self, конкретный, конкретный тип, реализующий протокол.ViewModelProtocol не сам по себе Equatable.Если бы это было так, какой код выше вы бы ожидали dataModel == myViewModel для вызова?Будет ли это проверить bar или нет?Помните, == это просто функция.Свифт не знает, что вы спрашиваете «равны ли они».Он не может вводить такие вещи, как «если они разные конкретные типы, они не равны».Это то, что сама функция должна делать.И поэтому Swift нужно знать, какую функцию вызывать.

Возможность сделать это в Swift станет важным дополнением к языку.Это обсуждалось довольно часто (см. Ссылку, которую я разместил в предыдущих комментариях), но это как минимум несколько выпусков.

Если вы удалите требование Equatable, оно все равно не будет работать сегодня, нотребуемая смена языка намного меньше (и может наступить раньше).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...