Почему протокол ViewModifier имеет связанный тип И типалиас?

Насколько мне известно, определение протокола ViewModifier выглядит следующим образом:

protocol ViewModifier {

    // content view type passed to body()
    typealias Content

    // type of view returned by body()
    associatedtype Body : View

    // only requirement
   func body(content: Self.Content) -> Self.Body

}

У меня вопрос:

Почему Self.Content - это typealias, а Self.Body - это associatedtype? Какая разница?


person lochiwei    schedule 10.09.2020    source источник
comment
Body будет выведен из вашего возврата, поэтому это связанный тип, Content будет предоставлен вам, он известен ViewModifer (потому что сгенерирован им), но скрыт для вас, так это typealias.   -  person Asperi    schedule 10.09.2020


Ответы (2)


Почему Self.Content - это typealias, а Self.Body - это associatedtype? Какая разница?

Поскольку Content - это typealias, автор протокола ViewModifier может выбрать тип, которому назначается псевдоним, когда она пишет протокол. (Вы не можете увидеть псевдоним типа, потому что это тип _ViewModifier_Content<Self>. Когда идентификатор в SDK начинается с _, Apple опускает идентификатор из документации и сгенерированных интерфейсов.)

Поскольку Body - это associatedtype, вы можете выбрать тип, которому он является псевдонимом, когда вы пишете тип, соответствующий протоколу ViewModifier. Вы можете сделать Body любым типом, который захотите, при соблюдении двух условий:

  • Вы должны выбрать тип, соответствующий View (поскольку протокол ViewModifier ограничивает Body соответствие View).

  • У вас должна быть возможность создать или получить экземпляр любого выбранного типа, потому что вы должны вернуть его из метода body. (Или вы можете разбиться или зависнуть, чтобы вообще не возвращаться, но обычно это не то, что вам нужно ...)

Итак, когда вы реализуете тип, соответствующий ViewModifier, вы не можете повлиять на то, что Content означает. Это всегда означает _ViewModifier_Content<Self>. Но вы можете выбрать, что означает Body, выбрав тип возврата метода body.

Вот я заставлю Body означать EmptyView:

struct EmptyModifier: ViewModifier {
    func body(content: Content) -> EmptyView {
        EmptyView()
    }
}

И здесь я заставлю Body означать Color:

struct RedModifier: ViewModifier {
    func body(content: Content) -> Color {
        Color.red
    }
}

Обычно мы используем some View в качестве типа, что означает, что Swift определяет для нас точный тип и хранит его в секрете:

struct FrameModifier: ViewModifier {
    var color: Color
    var width: CGFloat

    func body(content: Content) -> some View {
        return content
            .padding(width)
            .border(color, width: width)
    }
}

Здесь все, что мы знаем о типе Body, это то, что он соответствует View. Swift изрядно пытается помешать нам узнать настоящий тип во время компиляции.

person rob mayoff    schedule 10.09.2020
comment
Несмотря на вашу подробную информацию, я все еще не совсем понимаю одну вещь. Насколько я понимаю, нам не нужно указывать тип, который возвращает метод body(), SwiftUI сделает это за нас. В таком случае, почему бы просто не сделать Body тоже typealias? - person lochiwei; 11.09.2020
comment
Протокол полностью определяет typealias, но только ограничивает определение associatedtype. Каждое соответствие полностью определяет associatedtype по-своему. В этом разница между typealias и associatedtype. Мои примеры EmptyModifier, RedModifier и FrameModifier представляют собой три разных соответствия. Каждое соответствие определяет Body по-разному. Если бы Body был typealias, он был бы полностью определен ViewModifier, и мои три соответствия не могли бы дать ему три разных определения. - person rob mayoff; 11.09.2020

  • typealias изменяет только имя типа. Больше ничего.

  • associatedtype - это способ включить универсальные шаблоны в реализацию протокола.


public protocol ViewModifier {

    /// The type of view representing the body of `Self`.
    associatedtype Body : View

    /// Returns the current body of `self`. `content` is a proxy for
    /// the view that will have the modifier represented by `Self`
    /// applied to it.
    func body(content: Self.Content) -> Self.Body

    /// The content view type passed to `body()`.
    typealias Content
}

В приведенном выше примере Body - это конкретный тип, соответствующий View, и определяется типом возвращаемого значения body(content:).

Content - это просто еще одно имя для типа, переданного как параметр content.


В этом ответе Роба Мэйоффа ViewModifier объясняется более подробно:

Таким образом, это говорит нам о том, что, когда мы пишем наш собственный ViewModifier, наш body метод получит какой-то View (конкретный тип определяется структурой, и мы можем просто назвать его Content), и вернем какой-то View (мы получаем выберите конкретный тип возврата).

Это означает, что вы не знаете, что такое Content, вы просто работаете с ним - он называется Content для вашего удобства, поэтому вам не нужно иметь дело с _ViewModifier_Content<Self>.

person pawello2222    schedule 10.09.2020
comment
связанный тип - это способ включения универсальных шаблонов в реализацию протокола Это просто способ включения другого типа класса / протокола. например вы также можете использовать UIView, который не является протоколом ... - person Honey; 10.09.2020
comment
Content - это просто другое имя для кого? View? или что-то другое? Не могли бы вы привести конкретный пример и показать мне, что такое конкретный тип Content? - person lochiwei; 10.09.2020