Значимые и ссылочные типы в Swift: какой тип выбрать?

В языке программирования Swift некоторые типы являются значимыми, а другие являются ссылочными. Какая между ними разница? И как эти типы влияют на разработку под iOS?

Типы в Swift

Давайте освежим в памяти, что из себя представляют типы данных. Вы уже знаете, что у каждой переменной и константы есть свой тип. Целое число имеет тип Int, текстовая строка имеет тип String и т. д.

let age: Int = 42

В приведенном выше примере значение 42 типа Int присваивается константе с именем age. Сам по себе тип Int является структурой.

Мы можем использовать множество различных типов, таких как перечисления, структуры, опционалы, замыкания, кортежи, классы и типы функций. Короче говоря, в программировании мы сохраняем объекты, и эти вещи имеют разные имена и типы.

В этой статье мы обсуждаем конкретный аспект типов: является ли тип значимым или ссылочным. Это различие напрямую влияет на код, который вы пишете, поэтому хорошо понимать разницу между ними.

Простое объяснение состоит в том, что значимые типы сохраняют уникальную копию своих данных, тогда как ссылочные типы совместно используют одну копию данных.

Значимые типы

var a = 99
var b = a
b += 1
 
print(a) // 99
print(b) // 100
  • Сначала значение 99 присваивается переменной a. Тип этой константы Int.
  • Затем создается новая переменная b. Значение a копируется, и обе переменных теперь сохраняют уникальную копию значения 99.
  • Затем 1 добавляется к b, так что константа b теперь имеет значение 100. Константа a все еще равна 99.

Значение константы a копируется в константы b, при назначении a = b. Это создает вторую копию значения 99.

При этом значение a не изменяется при добавлении 1 в b. a и b обладают уникальными значениями. Это отличительная черта значимых типов.

значимые типы

В Swift следующие типы являются значимыми:

  • Структуры, такие как Int, String, Double и Bool.
  • Массивы, словари и множества.
  • Перечисления и кортежи.

Swift предпочитает использовать значимые типы по сравнению со ссылочными. Значимый тип не может быть изменен другим потоком или процессом, и это делает наш код менее подверженным ошибкам.

Ссылочные типы

Значимые типы копируются и сохраняют уникальную копию данных. Однако ссылочные типы работают совсем по-другому. Ссылочный тип сохраняет ссылку на один экземпляр всякий раз, когда он назначается новой переменной или константе.

class Car {
    var speed = 0
}
 
let racecar = Car()
racecar.speed = 250
 
let bus = racecar
bus.speed = 40
 
print(racecar.speed) // 40
print(bus.speed) // 40
  • Сначала мы создаем класс с именем Car, который имеет одно свойство speed типа Int со значением 0.
  • Затем мы создаем экземпляр Car и присваиваем его константе racecar. Затем мы устанавливаем свойство speed racecar в 250.
  • Далее мы присваиваем racecar новой констнате bus. Мы также устанавливаем свойство speed для bus 40.

Класс Car является ссылочным типом. В отличие от значимых типов, ссылочные типы не копируются, когда назначаются новой переменной или константе. Вместо этого копируется ссылка на экземпляр. Эта ссылка указывает на один и тот же адрес в памяти. Это означает, что обе константы указывают на один и тот же экземпляр. Можно сказать, что обе константы совместно используют один и тот же экземпляр.

ссылочные типы

В Swift следующие типы являются ссылочными:

  • Классы.
  • Функции.
  • Замыкания.

let и var

Если вы внимательно посмотрите на предыдущий пример, вы увидите что-то странное. Обратите внимание, что racecar определяется как let, так что это константа. Константы не могут изменить свое значение.

let и var работают по-разному для значимых и и ссылочных типов:

  • Для ссылочных типов сама ссылка должна оставаться постоянной. Таким образом, вы можете изменить экземпляр, как и его свойства, но вы не сможете изменить ссылку.
  • Для значимых типов само значение должно оставаться постоянным. Таким образом, вы не можете изменить значение или любое из его свойств. Значения являются неизменными.

Из этого мы можем сделать вывод, что намного проще контролировать значимые типы. Как только константа объявлена, и это значимый тип, вы можете быть на 100% уверены, что данные не изменяться. При этом вы всегда можете изменить свойства ссылочного типа, даже если он объявлен с помощью let.

Значимые и ссылочные типы в разработке

Рассмотрим сценарии в разработке, в которых важно учитывать значимые и ссылочные типы:

  • Когда вы используете определенный API, вы должны следовать методикам этого API и учитывать, какие типы являются значимыми, а какие ссылочными.
  • Когда вы проектируете свои собственные типы, вам нужно решить, должен ли тип быть значимым или ссылочным.

В первом сценарии работать со значимыми и ссылочными типами довольно просто. Вам просто нужно следовать API. Например, в Swift большинство простых типов являются значимыми типами. И работая с чем-то вроде Realm , вы увидите, что постоянные объекты являются значимыми типами (которые ссылаются на данные базы данных).

Особый сценарий происходит при работе с Objective-C. Как вы, возможно, знаете, многие типы в Objective-C соединены со Swift. Например, класс NSString соединен со структурой String в Swift. Этот мост неявный, поэтому, хотя Objective-C имеет только ссылочные типы, тип в Swift все еще является значимым типом. Однако многие другие API Objective-C являются производными от класса NSObject и поэтому также являются ссылочными типами в Swift.

Но что, если вы пишете свои собственные классы, структуры, перечисления или кортежи? Когда следует выбирать значимый тип, например, структуру, а когда — ссылочный тип, например, класс?

Вы можете выбрать между значимыми типами значений и ссылочными на основании:

  • Когда вы сравниваете, два объекта равны между собой или идентичны.
  • Должен ли тип иметь общее или независимое состояние.

Определение равенства или идентичности

Предположим, что мы создаем тип DollarBill. Должен ли этот тип быть структурой или классом?

Чтобы решить, давайте сначала выясним, как мы собираемся определять, что две долларовые банкноты одинаковы. Мы можем сделать это двумя способами:

  • Две долларовые банкноты одинаковы, если имеют одинаковую денежную стоимость, например 20 долларов. В коде это может выражаться через isEqual: a.value == b.value с помощью оператором равенства.
  • Две долларовые банкноты одинаковы, если они ссылаются на одну и ту же физическую, уникальную долларовую банкноту. В коде это может выражаться через isEqual: a === b с помощью оператора идентичности.

Копировать долларовые купюры не имеет смысла, поэтому разумно выбрать здесь ссылочный тип, то есть класс. Таким образом, мы можем быть на 100% уверены, что любая ссылка на долларовую банкноту относится к фактической. Когда в вашем коде передается долларовая банкнота, вы можете быть уверены, что дополнительные копии не создаются.

Совместное или независимое состояние

Давайте посмотрим на другой пример. Предоположим, что мы создаем систему учета с Invoice и Company типами. Каждый счет имеет ассоциированную компанию, в которую счет отправляется. Какими типами должны быть Invoice и Company?

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

Но как насчет Company? С одной стороны, разумно иметь несколько счетов, относящихся к одной и той же компании. Когда компания меняет свое имя или адрес, вы можете просто обновить один Company объект, и каждый счет будет обновлен автоматически.

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

В этом сценарии экземпляры Invoice и Company не разделяют одно и то же состояние. Одна и та же компания, связанная с двумя счетами, на самом деле является двумя разными компаниями с точки зрения счета. У них есть независимые состояния — изменение одного не меняет другого. Поэтому мы выбираем значимые типы.

Читайте также:
Добавить комментарий

Ваш адрес email не будет опубликован.