Что такое reference types
Reference types (ссылочные типы) — это типы данных в Swift (и других языках программирования), значения которых не копируются при передаче, а передаются по ссылке. Это означает, что если объект ссылочного типа присваивается другой переменной или передаётся в функцию, то обе переменные будут указывать на один и тот же объект в памяти. Изменения, сделанные через одну переменную, будут видны через другую.
📌 Основная идея ссылочных типов
В отличие от value types (значимых типов), где каждая переменная содержит собственную копию данных, ссылочные типы работают с указателями на одну и ту же область памяти, где хранятся данные.
Пример:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
var personA = Person(name: "Alice")
var personB = personA
personB.name = "Bob"
print(personA.name) // Выведет "Bob"
Здесь personA и personB указывают на один и тот же объект в памяти. Изменение имени через personB отразилось и на personA.
📦 Типы, являющиеся ссылочными в Swift
class
Классы в Swift являются reference types.
class Car {
var brand: String
init(brand: String) {
self.brand = brand
}
}
Когда вы создаёте объект Car, он хранится в куче, и переменные содержат ссылку на эту область памяти.
NSObject, UIView, URLSession, NSFileManager, и т.п.
Большинство классов из фреймворков Apple — это ссылочные типы, поскольку они реализованы на базе NSObject.
🧠 Как работает передача по ссылке
Когда переменная ссылочного типа присваивается другой переменной:
- **Не создаётся новый объект.
** -
Оба имени (ссылки) указывают на один и тот же экземпляр.
-
Изменения через одну ссылку отражаются на всех остальных.
Это поведение особенно важно учитывать при работе с коллекциями, делегатами и обработкой состояния.
📊 Память и ARC
Ссылочные типы в Swift управляются с помощью Automatic Reference Counting (ARC):
-
Каждый объект имеет счётчик ссылок.
-
Когда счётчик становится равным нулю — память освобождается.
-
Сильные ссылки (strong) увеличивают счётчик.
-
Слабые (weak) и безвладельческие (unowned) — не увеличивают счётчик.
Пример:
class Dog {
var name: String
init(name: String) {
self.name = name
print("Dog \\(name) создан")
}
deinit {
print("Dog \\(name) уничтожен")
}
}
var dog1: Dog? = Dog(name: "Rex")
var dog2 = dog1 // Счётчик: 2
dog1 = nil // Счётчик: 1
dog2 = nil // Счётчик: 0 → deinit вызывается
⚠️ Утечки памяти и циклы
Главная проблема с ссылочными типами — retain cycles (циклические сильные ссылки):
class Parent {
var child: Child?
}
class Child {
var parent: Parent?
}
let p = Parent()
let c = Child()
p.child = c
c.parent = p // retain cycle: объекты никогда не освободятся
Решение: использовать weak или unowned ссылки.
🏗 Отличия от value types
Характеристика | Reference Type | Value Type |
---|---|---|
Передача | По ссылке | По значению (копия) |
--- | --- | --- |
Изменения влияют на копии? | Да | Нет |
--- | --- | --- |
Примеры | class, NSObject, UIView, URLSession | struct, enum, Int, String, Array |
--- | --- | --- |
Хранение | В куче (heap) | В стеке (stack) или куче (COW) |
--- | --- | --- |
Управление памятью | ARC | Автоматическое |
--- | --- | --- |
📍 Поведение при параметрах функции
func updateName(person: Person) {
person.name = "Charlie"
}
let user = Person(name: "Dave")
updateName(person: user)
print(user.name) // Charlie — объект изменён, потому что передан по ссылке
Функция updateName модифицирует объект user, несмотря на отсутствие inout, поскольку классы — ссылочные типы.
🔁 Присваивание и сравнение
Присваивание:
let obj1 = Person(name: "A")
let obj2 = obj1
// obj1 и obj2 указывают на один объект
Сравнение:
if obj1 === obj2 {
print("Это один и тот же экземпляр")
}
Оператор === сравнивает ссылки на объект, а не содержимое.
🧬 Reference Semantics
Смысл ссылочной семантики — разделение данных между участниками программы. Это удобно в ряде сценариев:
-
Использование делегатов или замыканий, которым нужна ссылка на "родителя".
-
Моделирование объектов в бизнес-логике, которые изменяются в одном месте и становятся доступными в другом.
-
Совместное использование ресурсов (например, кэш, пул подключений).
🧰 Когда использовать классы (reference types)
Используйте class, если:
-
Объекты должны разделять состояние (shared mutable state).
-
Вы хотите использовать наследование.
-
Необходимо изменять данные в нескольких местах одновременно.
-
Вы работаете с API Apple, которые требуют классов (UIKit, Foundation и т. д.).
-
Требуется ARC и управление временем жизни объекта.
🧩 Пример со структурой и классом:
struct StructPoint {
var x: Int
var y: Int
}
class ClassPoint {
var x: Int
var y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
var a = StructPoint(x: 0, y: 0)
var b = a
b.x = 10
print(a.x) // 0
var c = ClassPoint(x: 0, y: 0)
var d = c
d.x = 10
print(c.x) // 10
🔄 Типы ссылок
Swift поддерживает несколько модификаторов ссылок:
-
strong (по умолчанию) — увеличивает счётчик ссылок.
-
weak — не увеличивает счётчик; применяется к опционалам.
-
unowned — не увеличивает счётчик; никогда не должен быть nil.
class Owner {
var pet: Pet?
}
class Pet {
weak var owner: Owner?
}
Таким образом, reference types — это важная категория типов, основанная на ссылочной семантике и управляемая через ARC. Их использование требует понимания особенностей управления памятью и копирования, особенно в многопоточной или архитектурно сложной среде.