Основы Swift / 11.3. Классы как ссылочные типы


Видео


Ссылочные типы
В отличие от типов-значений ссылочные типы не копируются, когда они признаются переменной или константе или когда передаются в функцию. Вместо копирования используется ссылка на тот же самый существующий объект.
@{11.3\1\1}
let tenEighty = VidoeMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
В этом примере объявляется новая переменная с названием tenEighty и устанавливается ссылкой в новый экземпляр класса VideoMode. Режиму экрана присваивается копия HD-разрешения из 1920x1080 пикселей из примера ранее. Режим экрана устанавливается в чрезстрочный с названием "1080i". В конце концов ему устанавливается частота 25.0 кадров в секунду.
@{11.3\1\2}
Затем tenEighty присваивается новой константе с названием alsoTenEighty, и частота кадров alsoTenEighty изменяется:
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
Так как классы - это ссылочные типы, то tenEighty и alsoTenEighty на самом деле оба ссылаются на один и тот же объект типа VideoMode. Фактически, они оба просто два разных имени для одного и того же объекта.
@{11.3\1\3}
Проверив свойство frameRate у tenEighty, мы увидим, что оно корректно отображает новую частоту кадров 30.0:
print("свойство frameRate у tenEighty теперь равно \(tenEighty.frameRate)")
// Выводит "свойство frameRate у tenEighty теперь равно 30.0"
Заметьте, что tenEighty и alsoTenEighty объявлены как константы, а не как переменные. Однако, Вы всё ещё можете изменить свойства tenEighty.frameRate и alsoTenEighty.frameRate, так как как деле значения tenEighty и alsoTenEighty не изменились. tenEighty и alsoTenEighty сами по себе не хранят объект типа VideoMode - вместо этого они оба ссылаются на объект типа VideoMode. И фактически меняется свойство у frameRate у объекта VideoMode, а не значения констант, ссылающихся на этот VideoMode.
Операторы идентичности
Так как классы - это ссылочные типы, то существует возможность для нескольких констант и переменных ссылаться на один и тот же экземпляр класса. (Это неверно для структур и перечислений, так как они всегда копируются, когда признаются константе или переменной или передаются в функцию.)

Это может быть иногда полезным определить, являются ли две константы или переменные ссылками на один и тот же экземпляр класса. Чтобы обеспечить это, Swift предоставляет два оператора идентичности:
  • Идентичен (===)
  • Не идентичен (!==)
Используйте эти операторы для проверки того, ссылаются ли две константы или переменные на один и тот же объект:
@{11.3\1\4}
if tenEighty === alsoTenEighty {
    print("tenEighty и alsoTenEighty ссылаются на один объект типа VideoMode.")
}
//Выводит "tenEighty и alsoTenEighty ссылаются на один объект типа VideoMode."
Разница между идентичностью и эквивалентностью
Заметьте, что оператор идентичности (записываемый в виде трёх знаков равенства или ===) делает не то же самое, что и оператор эквивалентности (записываемый в виде двух знаков равенства или ==):

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

Когда Вы определяете Ваши собственные классы и структуры, это Ваша ответственность решить, что определяет два объекта как равные.
Указатели
Если Вы имели опыт с C, C++ или Objective-C, Вы можете знать, что эти языки используют указатели для ссылки на адреса в памяти. Константы или переменные в Swift, которые ссылаются на объект некого ссылочного типа схожи с указателями в C, но это не прямой указатель на адрес в памяти, и Вам не нужно писать астерикс (*) для указания того, что Вы создаёте ссылку. Вместо этого эти ссылки определяются как и любые другие константы или переменные в Swift.
Выбор между классами и структурами
Вы можете использовать и классы, и структуры для определения Ваших собственных типов данных для использования в построении блоков Вашей программы.

Однако структуры-объекты всегда передают по значению, а объекты классов всегда передаются по ссылке. Это обозначает, что они предназначены для разных видов зада. Когда Вы определите конструкции данных и функциональность, нужную Вам для проекта, решите для каждой конструкции данных, должна ли она быть определена как класс или как структура.

В качестве главного совета, создание структуры предпочтительнее, когда одно или более из этих условий применимы:
  • Основная цель структуры - инкапсулировать несколько связанных простых значений данных.
  • Есть все основания ожидать, что инкапсулированные значения будут передавать по значению, а не с помощью ссылочного механизма, когда Вы присваиваете или передаёте объект этой структуры.
  • Все свойства, хранимые структурой являются типами-значениями, что ведёт к тому, что они будут копироваться, а не ссылаться.
  • Структуре не нужно наследовать свойства или поведение из другого существующего типа.
Примеры хороших кандидатов на роль структуры:
  • Размер геометрической фигуры, возможно инкапсулирущей свойства @code(width) и @code(height), оба типа @code(Double).
  • Способ сослаться на диапазон значений, возможно инкапсулирующий свойства @code(start) и @code(end), оба типа @code(Int).
  • Точка в трёхмерной системе координат, возможно инкапсулирующая свойства x, y и z, все типа @code(Double).
Во всех остальных свойствах определите класс и создавайте объекты этого класса, управляемые и передаваемые по ссылке. На практике это означает, что многие произвольные конструкции данных должны быть классами, а не структурами.
Поведение присваивания и копирования для строк, массивов и словарей
В Swift многие базовые типы, такие как String, Array и Dictionary реализованы как структуры. Это значит, что данные, такие как строки, массивы и словари, копируются, когда они признаются новой константе или переменной или когда они передаются в функцию или метод. Это поведение отлично от Foundation: NSString, NSArray и NSDictionary реализуются в качестве классов, а не структур. Строки, массивы и словари в Foundation всегда признаются и передаются в качестве ссылки на существующий экземпляр, а не на его копию.

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