Class — это тип-ссылка (reference type) в языке Swift, экземпляры которого передаются по ссылке, а не копируются при присваивании. В отличие от структур, классы поддерживают наследование, деинициализацию и автоматический подсчёт ссылок ARC для управления памятью. По данным Swift Programming Language Guide, 2026, class необходим для работы с UI-фреймворками Apple (UIKit, AppKit) и реализации паттернов, требующих общей идентичности объекта. Выбор между class и struct — одно из ключевых архитектурных решений в Swift.
Главное
Class (класс) — это составной ссылочный тип в Swift, который группирует свойства и методы в единую сущность с поддержкой наследования и динамической диспетчеризации. В отличие от struct, экземпляр класса создаётся в куче (heap), а переменная хранит ссылку на этот экземпляр, а не сами данные.
Когда вы присваиваете переменную класса другой переменной, обе ссылаются на один и тот же объект в памяти. Изменения через одну ссылку видны через другую — это фундаментальное свойство reference types, которое используется в паттернах делегирования, наблюдения и совместного состояния.
По данным Apple Swift Documentation, классы — единственный способ работать с UIKit и AppKit, где все UI-компоненты наследуются от UIView и UIViewController. Кроме того, классы необходимы для реализации паттернов, где требуется идентичность объекта (две ссылки на один объект) и контролируемое время жизни.
Reference type semantics — ключевое свойство классов. При присваивании класса новой переменной Swift копирует ссылку, а не данные. Все переменные, ссылающиеся на один экземпляр, видят его текущее состояние и могут его изменять. Это поведение принципиально отличается от value types, где каждая переменная получает независимую копию.
// Example of reference type semantics
class User {
var name: String
init(name: String) { self.name = name }
}
let user1 = User(name: "Alice")
let user2 = user1 // user2 — same reference
user2.name = "Bob"
print(user1.name) // "Bob" — changed via user2!
// Identity check with === operator
print(user1 === user2) // true — same object
Операторы === (identity) и !== проверяют, ссылаются ли две переменные на один и тот же экземпляр класса. Это отличие от == (equality), которое сравнивает значения свойств. Для struct оператор === недоступен — у value types нет идентичности.
Наследование — механизм, при котором класс может перенять свойства и методы родительского класса. В Swift класс может наследовать только от одного родителя (single inheritance), но может реализовывать множество протоколов. Ключевое слово override позволяет переопределить унаследованный метод или свойство.
Любой класс, не наследующий от другого класса, автоматически становится базовым (не путать с NSObject). Подкласс указывает родителя через двоеточие после имени. Если подкласс переопределяет метод родителя, он обязан вызвать super.метод() для сохранения родительского поведения — это обязательное требование компилятора.
Ключевое слово final перед class запрещает наследование. Компилятор может оптимизировать вызовы методов final-класса через static dispatch, что повышает производительность. Используйте final для классов, которые не предназначены для расширения — это документирует ваше намерение и ускоряет код.
// Example of class inheritance in Swift
class Vehicle {
var speed: Double = 0
func description() -> String {
"Speed: \(speed) km/h"
}
}
// Car inherits Vehicle
class Car: Vehicle {
var brand: String = "Unknown"
override func description() -> String {
"\(brand) - \(speed) km/h"
}
}
// Final class — prevents inheritance
final class ElectricCar: Car {
var batteryLevel: Double = 100
}
let tesla = ElectricCar()
tesla.brand = "Tesla"
tesla.speed = 120
print(tesla.description()) // "Tesla - 120.0 km/h"
Наследование классов — мощный, но ответственный механизм. Глубокая иерархия (5+ уровней) усложняет поддержку и тестирование. Для повторного использования функциональности без наследования используйте протоколы с extension и protocol-oriented programming — подход, который Apple продвигает как альтернативу глубоким class-иерархиям.
Swift использует ARC (Automatic Reference Counting) для управления памятью классов. Каждый экземпляр класса имеет счётчик сильных ссылок. При создании новой сильной ссылки счётчик увеличивается; при разрушении — уменьшается. Когда счётчик достигает нуля, память освобождается.
Deinit — метод, который вызывается автоматически перед освобождением экземпляра класса. В нём освобождают ресурсы: закрывают файлы, отписываются от уведомлений, останавливают таймеры. Deinit есть только у классов — у структур и перечислений его нет.
Для предотвращения циклов сильных ссылок (retain cycles) Swift предоставляет weak и unowned ссылки. Weak — опциональная ссылка, которая автоматически становится nil при освобождении объекта. Unowned — неопциональная, но ссылается на объект, который гарантированно живёт дольше текущего. Типичный retain cycle возникает между parent-child relationship: child хранит strong ссылку на parent.
// ARC and weak reference example
class Parent {
var name: String
var child: Child?
init(name: String) { self.name = name }
deinit { print("\(name) deallocated") }
}
class Child {
var name: String
weak var parent: Parent? // weak prevents retain cycle
init(name: String) { self.name = name }
deinit { print("\(name) deallocated") }
}
var parent: Parent? = Parent(name: "Anna")
parent?.child = Child(name: "Mia")
parent?.child?.parent = parent
parent = nil // Both objects deallocated!
// Without weak it would create a retain cycle
Всегда используйте weak для ссылок от дочернего объекта к родительскому и для capture-списков в замыканиях. Unowned используйте только когда уверены, что объект переживёт текущий контекст — неправильное использование unowned может привести к crash при обращении к освобождённой памяти.
Выбор между class и struct — архитектурное решение, которое влияет на производительность, безопасность потоков и дизайн API. Рассмотрим таблицу ключевых отличий, которая поможет принять правильное решение в каждом конкретном случае.
| Характеристика | class | struct |
|---|---|---|
| Тип | Reference type (ссылка) | Value type (копия) |
| Память | Heap (куча) + ARC | Stack (стек) / inlined |
| Наследование | Поддерживает (один родитель) | Не поддерживает |
| Deinit | Есть | Нет |
| Identity (===) | Поддерживает | Не поддерживает |
| Mutating | Не требуется (всегда mutating) | Только с mutating |
| Memberwise init | Не генерируется | Генерируется автоматически |
| Thread safety | Не гарантирована (shared state) | Гарантирована (копирование) |
Используйте классы, когда нужна общая идентичность (несколько частей кода работают с одним объектом), наследование или взаимодействие с Objective-C runtime. Для всего остального предпочтительны структуры — они быстрее, безопаснее в многопоточном коде и не требуют управления памятью.
Несмотря на рекомендацию Apple использовать struct по умолчанию, классы необходимы в нескольких конкретных сценариях. Рассмотрим каждый с практическими примерами.
Все UI-компоненты в iOS и macOS — классы, наследующие от UIView (iOS) или NSView (macOS). Вы не можете заменить UIViewController на структуру — он требует наследования и deinit для освобождения ресурсов. При работе с UIKit используйте классы для контроллеров, view и их делегатов.
Паттерн Singleton (один экземпляр класса на всё приложение) требует ссылочной семантики. Менеджеры: NetworkManager, SettingsManager, AnalyticsService — обычно реализуются как классы с shared static property. Структуры не подходят, так как каждая копия будет независимым экземпляром.
Когда объект должен быть единственным источником истины (single source of truth) и передаётся между модулями по ссылке — используйте class. Это относится к стейт-менеджменту, где изменение объекта в одном месте должно быть видно во всех зависимых компонентах. Для ObservableObject в SwiftUI классы обязательны.
// ObservableObject — class required for SwiftUI
import SwiftUI
class AppViewModel: ObservableObject {
@Published var isLoggedIn: Bool = false
@Published var username: String = ""
func login(user: String) {
isLoggedIn = true
username = user
}
}
// Usage in SwiftUI View
struct ContentView: View {
@StateObject var viewModel = AppViewModel()
var body: some View {
Text(viewModel.isLoggedIn ? "Welcome" : "Login")
}
}
Правило: если объект должен иметь идентичность (две ссылки -> один объект), быть единственным экземпляром или работать с UIKit/Objective-C — выбирайте class. Если объект просто содержит данные — выбирайте struct.
Часто задаваемые вопросы
Class — это ссылочный тип (reference type) в Swift, который поддерживает наследование, деинициализацию и ARC для управления памятью. Экземпляры классов хранятся в куче, а переменные содержат ссылку на объект, а не его копию.
Class — reference type (передаётся по ссылке), поддерживает наследование и deinit. Struct — value type (копируется), не поддерживает наследование, но реализует протоколы и получает memberwise init автоматически. Swift рекомендует struct как тип по умолчанию.
ARC (Automatic Reference Counting) — механизм управления памятью для классов Swift. Каждый экземпляр имеет счётчик сильных ссылок. При обнулении счётчика память освобождается. weak и unowned ссылки предотвращают retain cycles между объектами.
Deinit — метод класса, который вызывается автоматически перед освобождением его памяти. Используется для закрытия файлов, отписки от уведомлений и других завершающих операций. Deinit есть только у классов — у структур его нет.
Используйте class для UI-компонентов UIKit/AppKit, singleton, ObservableObject в SwiftUI, объектов с общей идентичностью (delegate, observer) и при работе с Objective-C runtime. Для моделей данных, DTO и конфигураций предпочтительна struct.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также