Что такое conditional conformance? Как его реализовать и для чего используют?ios-50

Conditional Conformance (условное соответствие) — это мощная фича Swift, позволяющая типам соответствовать протоколу только при выполнении определенных условий. Это особенно полезно для generic-типов.

Основная концепция

Условное соответствие позволяет объявить, что generic-тип соответствует протоколу только когда его параметры типа удовлетворяют определенным требованиям.

Базовый синтаксис

extension GenericType: SomeProtocol where TypeParameter: SomeCondition {
    // реализация требований протокола
}

Как реализовать Conditional Conformance

1. Для стандартных коллекций

Пример: сделаем массив Equatable, если его элементы Equatable

extension Array: Equatable where Element: Equatable {
    static func ==(lhs: Array, rhs: Array) -> Bool {
        guard lhs.count == rhs.count else { return false }
        for (l, r) in zip(lhs, rhs) {
            if l != r { return false }
        }
        return true
    }
}

2. Для собственных generic-типов

struct Box<T> {
    let value: T
}

extension Box: Equatable where T: Equatable {
    static func ==(lhs: Box, rhs: Box) -> Bool {
        return lhs.value == rhs.value
    }
}

Практическое применение

1. Сравнение коллекций

Без conditional conformance пришлось бы:

  • Либо реализовывать == для всех массивов
  • Либо делать ручное сравнение в каждом случае

2. JSON-сериализация

protocol JSONRepresentable {
    func toJSON() -> String
}

extension Array: JSONRepresentable where Element: JSONRepresentable {
    func toJSON() -> String {
        let items = self.map { $0.toJSON() }
        return "[\(items.joined(separator: ", "))]"
    }
}

3. Комбинирование протоколов

extension Collection where Element: Numeric {
    func sum() -> Element {
        return reduce(0, +)
    }
}

Где используется в стандартной библиотеке

  1. Optional соответствует Equatable, Hashable и др., если Wrapped тип соответствует
  2. Array, Dictionary, Set соответствуют различным протоколам при выполнении условий для их элементов
  3. Последовательности и коллекции имеют множество условных соответствий

Ограничения и особенности

  1. Рекурсивные требования:

    • Нельзя сделать условное соответствие, которое косвенно зависит от себя
  2. Множественные условия:

    • Можно комбинировать несколько условий через &
extension Container: SomeProtocol
where Element: Equatable & Hashable {
    // ...
}
  1. Доступность:
    • Полноценная поддержка появилась в Swift 4.1
    • Некоторые сложные случаи требуют Swift 5+

Пример из реальной практики

Реализация универсального кэша:

protocol Cacheable {
    associatedtype Key: Hashable
    associatedtype Value

    func get(_ key: Key) -> Value?
    func set(_ value: Value, for key: Key)
}

extension Dictionary: Cacheable where Key: Hashable {
    func get(_ key: Key) -> Value? {
        return self[key]
    }

    func set(_ value: Value, for key: Key) {
        self[key] = value
    }
}

Оптимизации компилятора

Swift использует conditional conformance для:

  • Устранения избыточных проверок типов
  • Специализации generic-кода
  • Улучшения производительности коллекций

Резюмируем:

Conditional Conformance — это мощный механизм Swift, который позволяет generic-типам автоматически получать функциональность протоколов при выполнении условий для их параметров. Это улучшает переиспользование кода, делает APIs более выразительными и позволяет системе типов работать более эффективно. Особенно полезно для коллекций, optional-типов и других абстракций, где поведение логически зависит от типов-параметров.