В Swift, когда тип-значение (value type) может вести себя как ссылочный тип (reference type)?

Ответ

Тип-значение в Swift может демонстрировать поведение, схожее со ссылочным типом, в следующих случаях:

  1. При использовании inout параметров: Функция получает неявную ссылку на исходную переменную, позволяя её модифицировать.

    func increment(_ value: inout Int) {
        value += 1
    }
    var number = 10
    increment(&number) // number теперь равно 11
  2. При захвате в замыкании без списка захвата: Если значение изменяется внутри замыкания, оно захватывается по ссылке, а не по копии.

    var counter = 0
    let closure = {
        counter += 1 // Захватывает 'counter' по ссылке, изменяя оригинал
    }
    closure()
    print(counter) // 1
  3. При помещении в ссылочный тип: Когда структура или перечисление становится свойством класса, оно хранится в куче вместе с экземпляром этого класса.

    class Container {
        var value: Int = 0 // Тип-значение Int размещается в куче как часть экземпляра Container.
    }
  4. При использовании @escaping замыканий: Изменяемые типы-значения, захваченные таким замыканием, требуют особого внимания, так как их время жизни может быть продлено.

Ключевое отличие: Даже в этих случаях семантика копирования (copy-on-write для структур) сохраняется. Поведение лишь напоминает ссылочное из-за работы с общим, а не уникальным, хранилищем данных.

Ответ 18+ 🔞

О, слушай, тут такая тема интересная про Swift, просто пиздец! Тип-значение, он же value type, вроде как должен быть святым и неприкосновенным, копироваться на каждом углу. Но нет, блядь, иногда он начинает выёбываться и вести себя как ссылочный, этакая мартышлюшка в пальто!

Вот смотри, в каких случаях эта хуйня происходит:

Раз — inout параметры. Это же просто пиздец, какой трюк! Ты в функцию передаёшь не копию, а, по сути, ссылку на оригинальную переменную. Как будто говоришь: «Вот, держи мой инт, и нахуй с ним делай что хочешь!». И функция его меняет, а у тебя в исходной переменной уже новое значение. Магия, блядь!

func increment(_ value: inout Int) {
    value += 1 // Ёбнули по оригиналу напрямую!
}
var number = 10
increment(&number) // number теперь 11, и ты сам от себя охуел

Два — захват в замыкании без списка. Вот это вообще ловушка для распиздяев! Создал замыкание, а оно, сука, взяло и схватило твою переменную за горло, по ссылке! И теперь любое изменение внутри замыкания бьёт прямиком в оригинал. Чувствуешь подвох? Я чувствую!

var counter = 0
let closure = {
    counter += 1 // Захватило 'counter' по ссылке, как пидарас шерстяной!
}
closure()
print(counter) // 1, вот тебе и тип-значение, блядь

Три — когда запихиваешь его в класс. Ну тут всё просто, ёпта. Класс живёт в куче. Всё, что у него внутри, тоже там болтается. Положил ты свою структурку Int как свойство класса — и всё, пиши пропало. Теперь она часть этой кучи, общее хранилище. Хуй с горы, не выковыряешь.

class Container {
    var value: Int = 0 // Этот интик теперь в куче, как приговорённый.
}

Четыре — @escaping замыкания. А вот это уже высший пилотаж, блядь. Замыкание сбежало, унеслось куда-то в асинхронщину, а твоё тип-значение в нём сидит. И живёт там, пока замыкание живо. Время жизни продлилось, волнение ебать! Надо очень чётко понимать, что ты там меняешь, а то пиздец наступит нежданно.

Но вот главная мысль, которую надо вбить в бошку, чтобы она думала: даже когда он так выёбывается, его семантика копирования никуда не делась! Для структур, например, срабатывает copy-on-write (COW). То есть пока ты не начал менять, все могут пользоваться одним хранилищем. А как только полез что-то писать — бац, и создаётся отдельная, уникальная копия. Так что это не чистая ссылочность, а такая, блядь, хитрая жопа, которая только напоминает её из-за работы с общим хранилищем. Вот такой вот пиздец, малята.