Protocol в Swift — что это, синтаксис и возможности

Автор: IT Sectr Опубликовано: 2026-06-17 Время чтения: 6 мин

Protocol в Swift — это контракт, определяющий набор требований, которым должен удовлетворять любой принявший его тип. Протоколы лежат в основе протокол-ориентированного программирования (POP) — парадигмы, рекомендованной Apple для Swift наряду с классическим ООП. По данным Apple Documentation, 2026, структуры, классы и enum могут принимать протоколы, что делает архитектуру гибкой и тестируемой.

Главное

  • Protocol — контракт с требованиями для структур, классов и enum
  • Requirements включают методы, свойства, инициализаторы и subscripts
  • Protocol Composition позволяет комбинировать несколько протоколов
  • POP — протокол-ориентированное программирование, альтернатива наследованию
  • Protocol Extensions добавляют реализации по умолчанию для методов

Что такое Protocol в Swift?

Protocol — это абстрактный интерфейс, определяющий, что должен уметь тип, но не как он это делает. В Swift протокол — аналог интерфейсов из Java или Go, но с дополнительными возможностями.

Протокол может требовать:

  • Методы экземпляра и типа (статический)
  • Свойства с указанием { get } или { get set }
  • Инициализаторы
  • Subscripts
  • Associated types (дженерики на уровне протокола)

По данным WWDC 2015, Apple представила Protocol-Oriented Programming как фундаментальный подход к архитектуре Swift-приложений. В отличие от ООП, где наследование классов создаёт жёсткие иерархии, POP предлагает композицию протоколов. Это даёт гибкость: один тип может соответствовать нескольким протоколам, получая их требования и реализации через extensions.

Синтаксис объявления протоколов

Protocol объявляется ключевым словом protocol с именем и телом.

swift
protocol Drawable {
    func draw(context: CGContext)
    var boundingBox: CGRect { get }
}

Тип принимает протокол через двоеточие после имени:

swift
struct Circle: Drawable {
    let center: CGPoint
    let radius: CGFloat

    func draw(context: CGContext) {
        context.addArc(center: center, radius: radius, ...)
    }

    var boundingBox: CGRect {
        CGRect(x: center.x - radius, y: center.y - radius,
               width: radius * 2, height: radius * 2)
    }
}

Один тип может принять несколько протоколов через запятую: struct MyType: ProtocolA, ProtocolB. Компилятор проверяет, что все требования каждого протокола выполнены.

Требования методов и свойств

Property requirements указываются с ключевым словом var и спецификаторами доступа { get } или { get set }.

swift
protocol UserProtocol {
    var name: String { get }
    var age: Int { get set }
    static var maxAge: Int { get }
    mutating func updateName(_ newName: String)
}

Ключевое слово mutating в протоколе означает, что метод может изменять self. Структуры должны реализовать такой метод как mutating, классы — могут опустить. Method requirements указываются с полной сигнатурой, включая метки параметров.

Наследование протоколов

Protocol может наследовать другой протокол, добавляя новые требования. Это создаёт иерархию контрактов без жёсткой привязки к классам.

swift
protocol Vehicle {
    var speed: Double { get set }
    func move()
}

protocol Flyable: Vehicle {
    var altitude: Double { get set }
    func takeOff()
}

Тип, принимающий Flyable, должен выполнить требования обоих протоколов — и Vehicle, и Flyable. Swift поддерживает множественное наследование протоколов: protocol A: B, C.

Protocol Composition

Protocol Composition — механизм, позволяющий указать, что тип должен соответствовать нескольким протоколам одновременно, без создания нового протокола-комбинации.

swift
func render(_ item: Drawable & Animatable) {
    item.draw(context: ...)
    item.animate(duration: 0.3)
}

Composition (&) работает в параметрах функций, переменных и generic-ограничениях. Компилятор проверяет, что переданный тип одновременно удовлетворяет всем протоколам. Composition часто используется в SwiftUI: some View — это generic с composition.

Protocol Composition предпочтительнее создания иерархии наследования: вместо protocol A: B, C можно принимать B & C непосредственно. Это даёт гибкость и снижает связанность. Composition особенно удобен в параметрах функций и generic-ограничениях, где нужно временно объединить требования без создания промежуточного протокола. Это снижает количество вспомогательных типов в коде.

Protocol как тип

Protocol может использоваться как тип переменной, параметра функции или элемента коллекции. Это называется existential type.

swift
var drawableItem: Drawable
let items: [Drawable] = [Circle(...), Rectangle(...)]

func process(drawables: [any Drawable]) {
    for item in drawables {
        item.draw(context: ...)
    }
}

Ключевое слово any (Swift 5.6+) явно указывает на existential. Без any компилятор выдаёт предупреждение. Swift также поддерживает some (opaque types) для сокрытия конкретного типа за протоколом — это стандартный приём в SwiftUI.

Типовые ошибки с протоколами

При работе с protocol разработчики часто допускают несколько повторяющихся ошибок.

  • Забыли про mutating — метод в протоколе помечен как mutating, а структура реализует его без ключевого слова
  • Protocol as type вместо generic — использование Drawable вместо <T: Drawable> там, где нужен конкретный тип
  • Circular protocol reference — протокол, ссылающийся сам на себя через associated type, без корректного разрешения
  • Class-only protocol — использование AnyObject без необходимости, что исключает struct и enum

Часто задаваемые вопросы

Чем protocol отличается от abstract class?

Protocol определяет только требования без реализации (до protocol extensions). Abstract class может содержать реализацию и состояние. В Swift нет abstract классов — их роль выполняют протоколы с extensions.

Может ли struct принимать protocol?

Да, структуры, классы и enum — все могут принимать протоколы. Это основа POP: вы не обязаны использовать классы для организации архитектуры. Структуры с протоколами дают value semantics и иммутабельность.

Что такое associated type в протоколе?

Associated type (associatedtype) — placeholder для типа, который реализующий тип определяет самостоятельно. Пример: Collection имеет associatedtype Element. Это generic на уровне протокола.

Можно ли ограничить протокол только для классов?

Да, добавьте наследование от AnyObject: protocol MyProtocol: AnyObject. Тогда только классы смогут принять этот протокол. Это полезно для weak-ссылок и делегатов.

Как проверить соответствие протоколу в runtime?

Используйте is для проверки и as? для приведения: if let drawable = item as? Drawable. Это работает только для протоколов без associated types.

Итоги

  • Protocol — контракт для методов, свойств, инициализаторов и subscripts
  • Class, struct, enum — все типы могут принимать протоколы
  • Наследование протоколов создаёт иерархию контрактов без жёсткой связанности
  • Protocol Composition (&) комбинирует протоколы без наследования
  • Existential types (any) и opaque types (some) — два способа работы с протоколами
  • POP рекомендован Apple как альтернатива классическому наследованию классов
  • Protocol Extensions позволяют добавлять реализации по умолчанию

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также