Переменные и константы
Любая программа или приложение должны хранить данные. Этими данными могут быть любимый цвет пользователя, содержимое корзины, имя пользователя или что-то подобное. В Swift есть два способа хранения значений в вашем приложении:
- использование переменных;
- использование констант.
Переменная — это сущность, у которой значение можно изменить позже. Константа — это сущность, значение которой нельзя изменить позже.
Что из себя представляют переменные и константы?
Переме́нная — поименованная, либо адресуемая иным способом область памяти, адрес которой можно использовать для осуществления доступа к данным и обладающая некоторым типом. Данные, находящиеся в переменной (то есть по данному адресу памяти), называются значением этой переменной.
Переменная простыми словами — это хранилище данных. Сюда можно положить какое-то значение (например, число, строку или другой тип данных).
Наглядный пример
Еще более простой вариант представить себе переменную — подумать о том, что нас окружает. Например, переменной может быть какая-то коробка, куда можно положить все что угодно. Это может быть мяч, телефон, капуста и прочее.
Определяем, что в коробке будем хранить фрукты. На этой коробке написано «Яблоки» и это делает эту коробку уникальной. Всегда можно найти именно эту коробку по этому имени (при условии, что не может существовать ни одной другой коробочки с таким именем).
И в дальнейшем можно как угодно работать с этой коробкой. В любой момент можно положить туда что-нибудь (предварительно выкинув оттуда то, что там лежало раньше), посмотреть, что там лежит, или вообще взять и выкинуть эту коробку со всем содержимым.
Положим в коробку яблоко. Раз определили, что в коробке будут храниться фрукты, то ничего кроме фруктов в неё нельзя поместить. Можно ли положить вместо яблока какой-то другой фрукт? Можно, но на коробке написано «Яблоки», поэтому, при работе с коробкой, ожидается, что в ней будут именно яблоки. Если в ней будут другие фрукты, то это может вызвать путаницу. На яблоки можно, например, посмотреть, изменить их количество или произвести с яблоком какие-то ещё действия.
Переменную в любой программе необходимо объявлять. То есть достать эту коробку перед тем, как положить в неё яблоко.
В современных приложениях переменных может быть сколько угодно много. Например, в одной коробке хранится яблоко, в другой — конфета.
Таким образом, коробка будет переменной; то, что может хранить коробка (“фрукты”) - будет называться типом данных переменной; название коробки (“Яблоки”) - это имя переменной; то, что лежит в коробке, будет называться значением переменной.
При этом важно понимать, что концепция переменных - это очень сложный механизм работы с памятью, который просто скрыт от программиста современными операционными системами и высокоуровневыми языками программирования.
Зачем нужны переменные и как они работают
Все элементы компьютера (ноутбука, смартфона) в общем случае можно разделить на три основных функциональных уровня: аппаратный, операционной системы и программный.
Аппаратный уровень представлен физическим оборудованием компьютера. Это та основа, на которой базируются остальные уровни. Самые важные элементы аппаратного уровня — это центральный процессор (CPU, Central Processing Unit) и оперативная память.
Процессор — сердце любого современного электронного устройства (персонального компьютера, планшета, смартфона и т.д.). Компьютеры постоянно обрабатывают числа и вычисляют значения, выполняя простые арифметические и логические операции. Все данные в любом компьютере превращаются в числа. Причем неважно, говорим о фотографии, песне или книге, — для компьютера все это лишь нули и единицы. CPU как раз и предназначен для работы с числами. Он получает на вход несколько значений и, в зависимости от переданной команды (инструкции), выполняет с ними заданные операции.
Данные, с которыми работает процессор, хранятся во временном хранилище — оперативной памяти.
Оперативная память — это основное средство хранения данных, используемое программами в ходе их функционирования. Память компьютера состоит из миллионов отдельных ячеек, каждая из которых может хранить небольшой объем информацию. Каждая ячейка имеет уникальный адрес, по которому ее можно найти в памяти. Но размер ячейки не позволяет хранить в ней большое количество данных, по этой причине ячейки логически объединяются в хранилища данных.
Хранилище может объединять произвольное количество ячеек, его размер определяется количеством записанной информации и при необходимости может изменяться. Как и ячейка памяти, хранилище имеет уникальный адрес, по которому может быть получено хранящееся в нем значение (обычно он соответствует адресу первой входящей в него ячейки).
Адреса ячеек (а соответственно и адреса хранилищ) — это наборы цифр, которые очень неудобны для использования программистами.
Примечание. Данное описание устройства и работы оперативной памяти упрощено для восприятия.
Во времена, когда программы писались на машинном коде, программист должен был запоминать в какой участок памяти он записал нужное значение. Представьте, как усложнялся процесс написания программы, когда возникала необходимость работы с несколькими значениями. Адрес байта памяти есть число, которое мало о чем говорит. Большой объем памяти создает трудности программисту.
С первыми языками программирования появилась полезная возможность связывания определенного участка оперативной памяти с символьным названием (набором символов). По сравнению с адресом, название переменной может отражать содержимое этого участка памяти. Кроме имени, у переменной есть её тип. Эти два свойства переменной (название и тип) определяют нужный участок памяти и способ его использования. В большинстве случаев именно тип переменной определяет сколько байтов памяти захватит переменная.
По этой причине Swift, как и любой другой язык высокого уровня, вместо числовых адресов позволяет указывать удобные и понятные имена (идентификаторы) для хранения данных в оперативной памяти. Такие имена использовать намного проще, чем запоминать и передавать в программу настоящие адреса ячеек.
По идентификатору можно не только получить значение, но и изменить его.
Примечание. Оперативная память хранит нужные данные так долго, как этого требует приложение (ограничиваясь лишь временем, пока оно запущено). Соответственно, если программа завершила выполнение своей работы, то ячейка памяти стирает свои данные и переменная исчезает. Для того, чтоб программа хранила свои данные и после завершения выполнения кода (для постоянного долговременного хранения информации) необходимо использовать постоянные места хранения - другие элементы аппаратного уровня, например HDD или SSD, сторонние серверы хранения и обменна данными.
Операционная система (ОС) — это посредник между программой и аппаратной частью. Благодаря ОС приложения могут использовать все возможности компьютера: воспроизводить музыку, отображать графику на экране, передавать данные по Wi-Fi и Bluetooth, выполнять математические операции и многое другое.
Операционная система предоставляет интерфейсы, которые используются в ходе работы программного обеспечения.
Программный уровень — это все программы, которые работают на вашем компьютере в среде операционной системы. Каждая программа решает множество задач и для этого взаимодействует с уровнем операционной системы и аппаратным уровнем.
- С целью решения задачи приложение может передать операционной системе команды.
- Система обрабатывает полученные данные и передает их на аппаратный уровень.
- Аппаратный уровень производит необходимые операции, используя при этом техническую составляющую устройства, после чего передает данные обратно на уровень операционной системы и далее на программный уровень.
Каждая задача, выполняемая приложением, в результате может привести к выполнению сотен и тысяч инструкций в процессоре.
Программный уровень подразумевает использование языка программирования, так как программы создаются именно с его помощью.
Каждая программа взаимодействует с данными, которые могут быть получены разными способами: загружены с жесткого диска, введены пользователем, получены из Сети и т.д. Часто эти данные нужно не просто получить, обработать и отправить в указанное место, но и сохранить, чтобы использовать в будущем. Так, например, получив ваше имя (предположим, что вы ввели его при открытии приложения), программа запишет его в память и при необходимости будет получать его оттуда, чтобы отобразить в соответствующем месте графического интерфейса.
Выше, когда мы говорили об оперативной памяти, то упоминали понятие хранилища данных.
Хранилище данных — это виртуальный объект со следующими свойствами:
- записанное значение;
- идентификатор (имя);
- тип информации, для хранения которой предназначено хранилище (числовой, строковый и др.).
В программировании на Swift при работе с хранилищами данных выделяют два важнейших понятия: объявление и инициализация.
- Объявление — это создание нового объекта (хранилища данных).
- Инициализация — это присвоение значения объекту.
Практически весь процесс программирования заключается в том, чтобы создавать (объявлять) объекты, задавать (инициализировать) им значения, получать эти значения из памяти и производить с ними операции.
В Swift выделяют два вида хранилищ данных:
- переменные, объявляемые с помощью ключевого слова
var
; - константы, объявляемые с помощью ключевого слова
let
.
Любое хранилище, неважно, какого вида, имеет три важнейших свойства:
- имя, по которому можно проинициализировать новое или получить записанное ранее значение. Ранее мы назвали его идентификатором;
- тип данных, определяющий множество значений, которые могут храниться в этом хранилище (целые числа, строки и т.д.);
- значение, которое в данный момент находится в хранилище.
В общем случае, переменная – это поименованный участок оперативной памяти, используемый для временного хранения данных.
В переменных хранятся определенные данные, которые можно впоследствии использовать в программе. Для того, чтобы переменная появилась, необходимо ее объявить (зарезервировать ячейку памяти под определенные данные).
Переменные в программе нужны не только для хранения данных, но и для различных операций с ними. Также в ходе выполнения программы можно, например, это значение достать и положить другое. Это будет значить, что переменная изменилась.
Такое объяснение достаточно близко к тому, как на самом деле устроено хранение в памяти компьютера: переменная — это выделенная область памяти, в которой хранятся наши данные. Константа — это особый тип переменной, значение которой нельзя изменить.
Итого:
- можно создавать и удалять переменные, в зависимости от потребности функционирования программы;
- в переменной можно хранить информацию;
- можно извлекать из переменной информацию, что не повлияет на значение самой переменной;
- в переменную можно записать новые данные.
Типы переменных. Статическая и динамическая типизация переменных
Как было отмечено выше, у переменной есть такое свойство, как тип.
Типизация — это прием, широко используемый в языках программирования для распознавания введенных переменных и определения того, как можно их далее использовать.
Если обратиться к примеру с коробкой выше, то типизация – это возможность разделить коробки по возможному содержимому. То есть, когда создаём коробку, то кроме имени указываем, что в ней может располагаться. И тогда, в коробку для телефона нельзя положить ничего другого.
Это позволяет дополнительно защититься от ошибок, потому что будет заранее известно, что будет в коробке, и будешь понятно, как нужно будет взаимодействовать с содержимым.
Типизация может быть как статическая — когда типы переменных задаются на этапе компиляции, так и динамическая — когда тип определяется и присваивается в ходе выполнения программы. В последнем случае иногда говорят, что переменная не имеет типа, хотя данные, содержащиеся в ней, безусловно, относятся к определённому типу данных, но выясняется это уже во время выполнения программы.
В большинстве случаев статическая типизация позволяет уменьшить затраты ресурсов при выполнении программы, поскольку для динамической типизации требуются затраты ресурсов на выяснение типов данных их приведение в выражениях с смешанными типами. Статическая типизация позволяет перекладывать проверку типов на этапе компиляции программы - статическая типизация позволяет экономить ресурсы при выполнении программы, так как все типы переменных известны заранее и нет необходимости вычислять их в ходе выполнения. Это также упрощает обнаружение ошибок ещё на этапе разработки, когда их исправление обходится менее дорого (можно предотвратить многие ошибки на этапе компиляции программы).
Язык Swift - является языком программирования со статической типизацией. Тип данных указывается явно или неявно. Изменить тип данных у переменной после его назначения - нельзя.
Более детально про типы данных можно узнать в отдельной статье. См. ссылки внизу.
Виды типов данных переменных
Данные для работы программы хранятся в оперативной памяти (ОЗУ). Условно в компьютере существует два вида памяти для работы приложения:
- Стек (Stack) – быстрая память, но сильно ограниченная по размеру;
- Куча (Heap) – память, ограниченная только размером оперативной памяти, но при этом значительно более медленная, чем стек.
Таким образом несложно понять, что стек хранит небольшие по размеру данные, к которым обеспечивается доступ с наибольшей скоростью, а в куче хранятся сложные сущности, содержащие большой объем данных.
В Swift есть два вида типов данных:
- Ссылочные (Reference Types) – хранятся в куче (сложные типы и классы);
- Значимые (Value Types) – хранятся в стеке (базовые примитивные типы).
Базовые типы данных относятся к значимым типам и хранятся в стеке.
Более детально про типы данных и работу с памятью можно узнать в отдельных статьях. См. ссылки внизу.
Локальные и глобальные переменные. Области видимости
Теперь стоит поговорить о таком понятии, как область видимости переменных, или просто область видимости.
Область видимости переменных — это область программы, в пределах которой имя переменной остается связанным с ее значением, то есть позволяет посредством себя обратиться к переменной. За пределами области видимости то же самое имя может быть связано с другой переменной или функцией либо быть свободным (не связанным ни с какой из них).
По зоне видимости различают локальные и глобальные переменные. Первые доступны только конкретной подпрограмме, вторые — во всей программе. С распространением модульного и объектного программирования, появились ещё и общие переменные (доступные для определённых уровней иерархии подпрограмм). Ограничение видимости может производиться путём введения пространств имён.
Ограничение зоны видимости придумали как для возможности использовать одинаковые имена переменных (что разумно, когда в разных подпрограммах переменные выполняют похожую функцию), так и для защиты от ошибок, связанных с неправомерным использованием переменных (правда, для этого программист должен владеть и пользоваться соответствующей логикой при структуризации данных).
Более детально про области видимости можно узнать в отдельной статье. См. ссылки внизу.
Как сказано выше, переменную можно создать, прочитать, изменить и удалить. Константу изменить нельзя.
Создание (Объявление) переменных и констант
Для того, чтоб работать с переменной, её необходимо сначала создать. Создание объектов (переменных, констант, функций, разных типов и пр.) называется объявлением. Объявляя какой-то объект, мы как бы говорим: “Создаю такой-то определенный объект”.
Общая форма объявления переменной (константы):
var number: Int = 10
Здесь объявляется переменная number
, которая имеет тип Int
. Затем она устанавливает значение константы - 10
.
var
— это ключевое слово, которое указывает на объявление переменной;number
— это имя переменной;:
— разделяет имя и тип переменной;Int
— это тип переменной;=
— используется для присвоения значения переменной;10
— это значение переменной.
После объявления - имя и тип данных изменить нельзя.
Итак, происходит три действия: объявление, определение типа и инициализация.
Объявление переменной или константы (Declaring Constants and Variables)
Объявляя переменную или константу мы сообщаем программе, что они сейчас будут созданы.
В Swift вы можете создавать переменные с помощью ключевого слова var
и константы с помощью ключевого слова let
.
Имя переменной или константы должно быть уникальным в той области видимости, в которой она объявлена. Возможность доступа к ячейке памяти по имени переменной или константе должна быть однозначной и конкретной.
Определение типа переменной или константы (Type annotations)
Swift - язык программирования со строгой типизацией. Это обозначает, что обязательно должен быть определен тип данных.
Если сразу указываем значение, то Swift автоматически определяет тип данных значения исходя из литералов значения. Если же значение не указано, то Swift не знает, какого типа данные в ней будут храниться и ему нужно явно указать этот тип.
Таким образом, объявить переменную можно и без указания значения. Но тогда нужно указать тип данных будущего значения - аннотация типа.
Например:
var welcomeMessage: String
В этом примере добавляется обозначение типа для переменной с именем welcomeMessage
, чтобы обозначить, что переменная может хранить строковый тип данных - String
. Теперь переменной welcomeMessage
можно присвоить любое текстовое значение, без каких либо ошибок:
welcomeMessage = "Hello"
Можно создать несколько переменных одного типа в одной строке. Для этого следует указать ключевое слово для объявления переменных (var
для переменных, let
для констант). Затем указать имена переменных, разделяя их запятыми. И после последнего имени переменной указать необходимый тип данных:
var red, green, blue: Double
Инициализация переменной или константы
Без присвоения начального значения переменная просто объявлена, а с начальным значением она еще и инициализирована.
Инициализация - это процесс назначения (присваивания) начального значения переменной. Таким образом при инициализации в память записываются какие-то определённые данные и работая с переменной - можно получить доступ к этим данным. Переменная при этом запущена в работу, ей присвоено начальное значение, она инициализирована.
Назначение значения называется присвоением значения и выполняется с помощью оператора присвоения =
.
Выражение “присвоить переменной х
значение value
” означает назначить значение value
переменной х
- х = value
.
Когда переменная объявлена без начального значения, т.е. она еще не инициализирована, не готова полностью к работе.
Три действия - объявление, определение типа, инициализация
Таким образом, есть три действия - объявление, определение типа и инициализация. Их можно записать в одном выражении:
var a: Int = 10
Неявное представление типа - без указания типа переменной. Тип переменной выбирает Swift на основание данных с которыми инициализируется переменная.
Swift рекомендует экономить время, если есть значение, он может самостоятельно определить тип данных.
var b = 5
В этом случае, Swift определит тип переменной исходя из типа присваиваемого значения. Если тип установить невозможно, то следует указывать тип данных переменной явно.
Обратите внимание, что “голого” объявления переменной не может быть. Должно быть обязательно объявление с какой-то второй операцией - или с присвоением значения или с определением типа данных значения. И можно применять сразу три действия, но нельзя только одно.
let a: Int = 10 // Объявление константы "а" (Полная форма)
var b = 5 // Объявление переменной "b" (Сокращенная форма)
var c: Int // Объявление переменной "с" с аннотацией типа
c = 5 // Присваивание переменной "c" значение "5"
var d = c // Инициализация копированием - переменная "d" проинициализирована со значением переменной "c"
Объявление переменной или константы происходит один раз в одной области видимости. Для дальнейшей работы (получения значение, присваивания нового значения) следует обратиться к переменной (константе) по имени.
После объявления переменной или константы изменить её тип данных нельзя.
Какие значения можно присвоить?
Можно присвоить те значения, тип данных которых соответствует типу данных переменной или константы. Если тип данных для переменной или константы при инициализации задаётся неявно - с помощью определения типа значения - то можно проинициализировать переменную (константу) со значением любого типа. В дальнейшем, после инициализации, переменной (константе) можно присвоить значение только с тем типом, с которым она была проинициализированна.
Переменной можно присвоить любые значения, которые могут быть сохранены в память. Эти значения можно указать с помощью литералов:
var name = "Adam"
var number = 10
Литералы — это представления фиксированных значений в программе. Это могут быть числа, символы, строки и т.д. Например, «Hello, World!», 12, 23.0, «C» и т.д.
Литералы часто используются для присвоения значений переменным или константам. Например:
let siteName = "Apple.com"
В приведенном выше выражении siteName
— это переменная, а "Apple.com"
— литерал.
Более детально про литералы можно узнать в отдельной статье. См. ссылки внизу.
Переменной можно присвоить значение другой переменной - инициализация (или присвоение) копированием:
var name = "Adam"
var nameTwo = name
print(name)
print(nameTwo)
// Output
// Adam
// Adam
Переменной можно присвоить значение результата выполнения операций, функций:
var a = 1
var b = 4
var c = a + 2 // c = 3
var d = a + b - c // d = 2
func addNumber() -> Int {
return 2 + 3
}
var e = addNumber() // e = 5
var f: Int = {
return 2 * 3
}()
Можно объявить несколько констант или несколько переменных на одной строке, разделяя их запятыми:
var x = 0.0, y = 0.0, z = 0.0
Можно объявить несколько констант или несколько переменных на одной строке, инициализируя с помощью кортежа:
var (g, h, i) = (10, 20, 30)
print(g)
print(h + i)
// Output
// 10
// 50
Переменные хранят разные значения. Выше были рассмотрены базовые типы данных. Теперь усложним.
var arrayString = ["Adam", "Bob", "Charlie"]
var arrayInt = [10, 12, 14]
var tuple = (10, 20, 30)
print(arrayString)
print(arrayInt)
print(tuple)
// Output
// ["Adam", "Bob", "Charlie"]
// [10, 12, 14]
// (10, 20, 30)
В этом примере были созданы переменные, которые хранят значение
arrayString
- массива строк (Array<String>
)arrayInt
- массива чисел (Array<Int>
)tuple
- кортежа ((Int, Int, Int)
)
Также переменные хранят значения экземпляров сложных типов:
struct Person {
var name: String
}
var objectOne = Person(name: "Jack")
var objectTwo: Person = Person(name: "Liam")
print(objectOne)
print(objectTwo)
// Output
// Person(name: "Jack")
// Person(name: "Liam")
Также переменная может хранить в себе ссылку на функцию:
func printSomeText() -> () {
return print("Some text")
}
let k: () = printSomeText()
var m = {
return 2 * 3
}
k
print(m())
// Output
// Some text
// 6
Здесь k
и m
хранят в себе ссылки на функции и их тип данных является функциональным.
Обратите внимание на переменную m
и f
из примера выше. Запишем обе переменные с их типом данных:
var f: Int = {
return 2 * 3
}()
// ...
var m: () -> Int = {
return 2 * 3
}
Переменная f
имеет тип Int
и значение было присвоено через анонимную функцию, которая вернула значение типа Int
. То есть значение f
- это число. Обратите внимание, что в конце функционального блока {}
указаны круглые скобки ()
.
Переменная m
имеет тип () -> Int
и является ссылкой на анонимную функцию (замыкающее выражение). Она возвращает значение с типом Int
. Переменная m
записана только с помощью блока кода {}
. Для обращения к переменной m
следует после её имени указывать круглые скобки ()
.
Более детально про функции и замыкания можно узнать в отдельных статьях. См. ссылки внизу.
Названия переменных. Соглашения об именах
Дать имя константе или переменной кажется простым. Однако, довольно сложно его правильно указать. Это связано с тем, что имя должно быть описательным и коротким одновременно.
Вот основные правила присвоения имён:
- Имена переменных и констант начинаются с маленькой буквы;
- Имя переменной должно быть таким, которое максимально понятно описывает эту переменную. Например,
numberofApples
— лучшее имя переменной, чемa
,apple
илиn
; - Хорошим тоном считается называть переменные английскими словами так, чтобы при взгляде на название стало сразу понятно, что хранится в этой переменной и для чего она нужна;
- Избегайте сокращенных имен. Даже если имена длинные, пишите их по буквам;
- Если имя переменной состоит из нескольких слов, то следует использовать так называемый “верблюжий регистр” (lowerCamelCase). Другими словами, следует начинать последовательные слова с заглавных букв, например,
myBigHouse
,addTwoNums
и т.д.; - Также нельзя использовать пробелы или специальные символы в именах переменных. Некоторые из них вызывают ошибки, а некоторые просто плохо выглядят.
Имена переменных должны начинаться либо с буквы, либо с символа подчеркивания _
, либо со знака доллара $
. Например,
// valid
var a = "hello"
var _a = "hello"
var $a = "hello"
Имена переменных не могут начинаться с цифр. Например,
// invalid
var 1a = "hello" // throws error
Swift чувствителен к регистру. Итак, А
и а
— разные переменные. Например,
var A = 5
var a = 55
print(A) // 5
print(a) // 55
Избегайте использования ключевых слов Swift, таких как var
, String
, class
и т.д., в качестве имен переменных.
Чтение и изменение переменных и констант
Для того, чтоб получить значение переменной или константы, следует обратиться к ней по имени
var name = "Bob"
print(name)
// Output
// Bob
В этом примере была объявлена переменная name
и потом было получено её значение в функции print()
.
Значение переменной или константы можно получить в той области видимости, в которой она объявлена.
Прежде, чем обращаться переменной или константе, она должна быть проинициализарована с каким-то значением:
var siteName: String
print(siteName) // Error: Variable 'siteName' used before being initialized
Для того, чтоб изменить значение переменной, следует обратиться к ней по имени и присвоить новое значение.
var numOne = 10
var numTwo = 15
print(numOne, numTwo)
// Output
// 10 15
numTwo = numOne + 100
print(numOne, numTwo)
// Output
// 10 110
В этом примере переменная numTwo
была инициализирована со значением 15
. Затем её значение было изменено на значение из выражения numOne + 100
- получили значение numOne
(10
) и добавили к нему 100
. Теперь значение numTwo
- 110
.
Внимание! Новое значение переменной должно быть того же типа, с которым переменная была объявлена. Тип данных переменной после её объявления изменить нельзя.
При этом обращаться к переменной, таким же образом как при объявлении этой переменной (с помощью ключевого слова var
) и указывать тип данных - нельзя. Это вызовет ошибку, так как программа будет воспринимать объявление - как создание новой переменной с тем же именем в той же области видимости
var name = "Bob"
print(name)
// Output
// Bob
name = "Charlie"
print(name)
// Output
// Charlie
var name = "Jack" // Error: Invalid redeclaration of 'name'
В этом примере переменная name
была проинициализирована со значением "Bob"
. Затем значение было изменено на "Charlie"
. Но при попытке изменить значение переменной с помощью конструкции var name = "Jack"
- фактически была попытка объявить новую переменную с тем же именем name
в той же области видимости. И это привело к ошибке.
Значение константы можно получить так же, как и у переменной - обратиться к константе по имени.
Разница между переменной и константой заключается в том, что переменная может быть изменена после инициализации, а константа — нет.
После объявления константы нельзя изменить ее значение. Например, рассмотрим следующий код:
let number = 0
number = 10
Этот код выдает ошибку:
Error: Cannot assign to value: 'number' is a 'let' constant
Константы полезны для значений, которые не будут меняться. Например, если вы моделируете самолет и вам нужно указать общее количество установленных кресел, вы можете использовать константу.
При этом константы потребляют меньше ресурсов компьютера. Поэтому необходимо использовать константы и только если в дальнейшем значение будет меняться - использовать переменные.
Удаление переменных и констант
Переменные и константы существуют до тех пор, пока существует область видимости, в которой они объявлены.
После выполнения кода в локальной области видимости происходит уничтожение объявленных в этой области видимости переменных и констант. Память, которую они занимали, освобождается.
Если переменные и константы были объявлены в глобальной области видимости, то они не уничтожаются до тех пор, пока программа работает.
Также существует захват значения, для того, чтоб сохранить значение после выполнения блока кода и закрытия области видимости.
Более детально про захват значения и работу с памятью можно узнать в отдельных статьях. См. ссылки внизу.
Вычисляемые переменные
Были рассмотрены переменные, которые хранят значения. Но переменные могут быть и вычисляемыми. Вычисляемые переменные не хранят значение, а вычисляют его каждый раз при обращении к ним.
Такие переменные объявляются следующим способом:
var variableComputed: Int {
get {
return value
}
set {
set value
}
}
В этом примере:
var
- ключевое слово для объявления переменной;variableComputed
- имя переменной;Int
- тип переменной;get {}
- блок кода, который возвращает значение переменной при обращении к ней;set {}
- блок кода, который выполняется при присвоении (установки) нового значения переменной.
Блок кода get {}
обязательно должен возвращать значение того типа, которое было определено для переменной.
Если значение переменной только для чтения, то блок set {}
не указывается. В этом случае можно не писать обертку get {}
, а только код с возвратом значения:
var variableComputed: Int {
return value
}
Пример:
var number: Int {
return 2 + 9
}
print(number)
// Output
// 11
Если в таком виде попытаться изменить значение, то будет ошибка, которая предупреждает о том, что значение только для чтения:
number = 40 // Error: Cannot assign to value: 'number' is a get-only property
Блок set {}
следует указывать, если необходимо выполнить какой-то определённый код при установке значения переменной.
var numberFirst = 2
var number: Int {
get {
return 2 + 9
}
set {
numberFirst = newValue
}
}
print(numberFirst)
print(number)
// Output
// 2
// 11
number = 40
print(numberFirst)
print(number)
// Output
// 40
// 11
В этом примере, newValue
- это имя по умолчанию для внутренней переменной в блоке set {}
, которое принимает то значение, которое назначается (устанавливается) переменной number
. Здесь, при установке значения переменной number
выполняется код, который меняет значение другой переменной numberFirst
. Напрямую изменить значение переменной number
нельзя, так как при чтении её значения срабатывает блок get {}
. Но можно сделать значение переменной number
зависимым от другой переменной и менять значение этой переменной в блоке set {}
. Тогда будет меняться и значение переменной number
:
var numberFirst = 2
var number: Int {
get {
return numberFirst + 9
}
set {
numberFirst = newValue - 9
}
}
print(numberFirst)
print(number)
// Output
// 2
// 11
number = 40
print(numberFirst)
print(number)
// Output
// 31
// 40
В этом примере, значение переменной number
зависит от переменной numberFirst
- это указано в блоке get {}
. В блоке set {}
указано изменение значение переменной numberFirst
в зависимости от устанавливаемого для переменной number
значения. А значит и значение для чтения переменной number
будет тоже меняться.
Можно заменить
newValue
на другое удобное имя, указав его в блокеset {}
следующим образом:set(numberValue) { code with numberValue }
Вычисляемые переменные в основном используются в пользовательских типах данных, таких как структуры, классы, перечисления. Там переменные называются свойствами.
Более детально про хранимые и вычисляемые свойства можно узнать в отдельных статьях. См. ссылки внизу.
Зачем нужны и переменные, и константы?
На этом этапе можно задаться вопросом, зачем нужны и переменные, и константы? Не проще ли было бы иметь только переменные? Тогда константа может быть переменной, значение которой вы никогда не измените.
Причина наличия обоих проста: программисты делают ошибки. Можно случайно изменить значение, которое не должно было изменяться. Это может полностью испортить приложение.
Также константы — базовое средство оптимизации используемых мощностей.
Поэтому рекомендуется использовать в основном константы, если инициализируемое значение не планируется изменять. И только, если в дальнейшем планируется изменение значения - использовать переменные.