Видеокурсы по изучению языка программирования Swift. Подробнее

Свойства хранения

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

В самой простой форме свойство хранения - константа или переменная, которая хранится как часть экземпляра определенного класса или структуры. Свойства хранения могут быть или переменными свойствами хранения (начинаются с ключевого слова var), или постоянными свойствами хранения (начинается с ключевого слова let).

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

Пример ниже объявляет структуру FixedLengthRange, которая описывает диапазон целых чисел и длина которой не может быть изменена после установки первого значения:

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// диапазон чисел 0, 1, 2
rangeOfThreeItems.firstValue = 6
// сейчас диапазон чисел 6, 7, 8

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

Свойства хранения постоянных экземпляров структуры

Если вы создаете экземпляр структуры и присваиваете его константе, то вы не можете изменять его свойства, даже если они объявлены как переменные:

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// теперь диапазон чисел выглядит как  0, 1, 2, 3
rangeOfFourItems.firstValue = 6
// это вызовет ошибку, даже несмотря на то, что firstValue переменная

Из-за того, что rangeOfFourItems объявлена в качестве константы (ключевое слово let), то невозможно поменять свойство firstValue, даже несмотря на то, что это свойство переменная.

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

Такое поведение не применимо к классам, так как они являются ссылочным типом. Если вы присваиваете экземпляр ссылочного типа константе, то он все еще может менять переменные свойства.

Ленивые свойства хранения

Ленивое свойство хранения - свойство, начальное значение которого не вычисляется до первого использования. Индикатор ленивого свойства - ключевое слово lazy.

Заметка

Всегда объявляйте свойства ленивого хранения как переменные (с помощью ключевого слова var), потому что ее значение может быть не получено до окончания инициализации. Свойства-константы всегда должны иметь значение до того, как закончится инициализация, следовательно они не могут быть объявлены как свойства ленивого хранения.

Ленивые свойства полезны, когда исходное значение свойства зависит от внешних факторов, значения которых неизвестны до окончания инициализации. Так же ленивые свойства полезны, когда начальное значение требует комплексных настроек или сложных вычислений, которые не должны быть проведены до того момента, пока они не понадобятся.

Пример ниже использует ленивое хранение свойства для избежания ненужной инициализации сложного класса. Этот пример объявляет два класса DataImporter и DataManager, но ни один из них мы не показали полностью:

class DataImporter {
    /*  
     DataImporter - класс для импорта данных с внешних источников
     Считаем, что классу требуется большое количество времени для инициализации
     */
    var fileName = "data.txt"
    // класс DataImporter функционал данных будет описан тут
}
 
class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // класс DataManager обеспечит необходимую функциональность тут
}
 
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// экземпляр класса DataImporter для свойства importer еще не создано

Класс DataManager хранит свойство data, которое инициализируется с новым пустым массивом значений типа String. Несмотря на то, что большая часть функциональности недоступна, цель класса DataManager - это управление и обеспечение доступа к этому массиву данных типа String.

Часть функциональности класса DataManager - это импорт данных из файла. Эта функциональность обеспечивается классом DataImporter, который, как мы подразумеваем, требует уйму времени для инициализации. Это может происходить из-за того, что экземпляр класса DataImporter должен открыть файл и прочитать его содержимое в памяти, когда DataImporter уже инициализирован.

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

Так как он создан как модификатор lazy, экземпляр DataImporter для свойства importer создается только тогда, когда впервые к нему обращаются, например когда запрашивается свойство fileName:

print(manager.importer.fileName)
// экземпляр DataImporter для свойства importer только что был создан
// Выведет "data.txt"

Заметка

Если к свойству обозначенному через модификатор lazy обращаются сразу с нескольких потоков единовременно, и если оно еще не было инициализировано, то нет никакой гарантии того, что оно будет инициализировано всего один раз.

Свойства хранения и переменные экземпляра

Если у вас есть опыт работы с Objective-C, вы может быть знаете, что существует два способа хранения значений и ссылок как часть экземпляра класса. В дополнение к свойствам вы можете использовать переменные экземпляра как резервное хранение для значений, в свойствах хранения.

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

 

Swift: 
4.0