Какие преимущества и недостатки у классов (class) в Swift?

Ответ

Преимущества:

  • Ссылочная семантика: Объекты передаются по ссылке. Это эффективно для больших данных и удобно для моделирования сущностей с общим, изменяемым состоянием.
  • Наследование: Позволяет создавать иерархии классов, переиспользуя и расширяя функциональность (single inheritance).
  • Полиморфизм: Объекты могут использоваться через интерфейс их родительского класса или протокола.
  • Жизненный цикл: Наличие деинициализатора (deinit) для освобождения ресурсов.
  • Идентичность: Возможность проверки идентичности объектов с помощью оператора ===.

Недостатки:

  • Риск цикла сильных ссылок (Retain Cycles): Требует аккуратного использования weak и unowned ссылок, особенно с замыканиями.
  • Сложность в многопоточности: Изменяемое состояние, доступное из нескольких потоков по ссылке, требует ручной синхронизации (мьютексы, семафоры).
  • Налог на выделение памяти: Объекты создаются в куче (heap), что медленнее, чем размещение в стеке (stack).
  • Отсутствие value-семантики по умолчанию: Изменение объекта по одной ссылке влияет на все другие ссылки.

Пример ссылочной семантики:

class User {
    var name: String
    init(name: String) { self.name = name }
}

let user1 = User(name: "Alice")
let user2 = user1 // Копируется ссылка, а не данные
user2.name = "Bob"
print(user1.name) // "Bob" - изменилось и у user1!

Ответ 18+ 🔞

А, ну вот, блядь, структуры в Swift! Это ж классика, ёпта. Сидишь такой, думаешь: "Ну что за хуйня, зачем они вообще нужны, если есть классы?". А потом вникаешь и понимаешь — охуенная штука, на самом деле.

Что у них хорошего, а?

  • Value-семантика, сука! Это когда ты передаёшь или присваиваешь свою структуру — она просто копируется, как будто её ксероксят. Получается две абсолютно независимых хуйни. Это, во-первых, предсказуемо, а во-вторых, по умолчанию потокобезопасно, если ты работаешь со своей локальной копией. Никаких сюрпризов, что кто-то на другом конце кода тебе данные испоганил.
  • Нет этих ёбаных retain cycles! Они ARC не используют, подсчёта ссылок нет. Забыл про weak, unowned — и спи спокойно.
  • Инициализатор тебе автоматом сгенерируют. Написал свойства — и всё, компилятор сам memberwise initializer сделает. Не надо руками эту скучную хуйню прописывать.
  • Быстрые, блядь! Для мелких данных они в стеке живут, а не в куче. А работа со стеком — это овердохуища быстрее, чем бегать по куче и память выделять.
  • Непоколебимые, как скала. По умолчанию иммутабельные. Хочешь поменять свой экземпляр изнутри метода? Так пометь его mutating, сука, чтобы все видели, что тут мутация происходит. Функциональный стиль прёт.

А что плохого?

  • Наследования нихуя нет. Нельзя взять и отнаследоваться, как от класса. Весь полиморфизм — только через протоколы, да.
  • Деинициализатора тоже нет. Нельзя код выполнить в момент, когда экземпляр на свалку истории отправляется.
  • Налог на копирование. Ну представь, структура на 50 полей, да ещё и вложенные структуры внутри. Каждое копирование — это пиздец какой объём работы. Хотя Swift хитрожопый, он использует Copy-on-Write (CoW), чтобы не копировать, пока реально не надо. Но всё равно, блядь, налог.
  • С идентичностью сложно. Два экземпляра с одинаковыми данными — они равны, но это две разные, блядь, копии. Не та же самая сущность, а её клон. Для моделирования каких-нибудь пользователей или заказов — не всегда удобно.

Вот, смотри, как эта value-семантика выглядит на практике:

struct Point {
    var x: Int
    var y: Int

    mutating func moveBy(x deltaX: Int, y deltaY: Int) {
        x += deltaX
        y += deltaY
    }
}

var p1 = Point(x: 0, y: 0)
let p2 = p1 // В этот момент, блядь, данные ксероксятся! Полный дубликат.
p1.moveBy(x: 10, y: 5) // Пихаем первую точку.
print(p2) // Point(x: 0, y: 0) - А вторая, сука, как была нулём, так и осталась! Независимая копия.

Вот и вся магия. Выбирай инструмент по задаче, а не потому что "ой, классы круче". Иногда структура — это именно то, что доктор прописал.