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 и
// rangeOfFourItems.firstValue = 6
// это выдаст ошибку, даже хотя firstValue - это переменное свойство
Так как rangeOfFourItems
объявлен в качестве константы, то невозможно изменить его свойство firstValue
, хотя оно и является переменным.
lazy
перед объявлением.
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
тогда и только тогда, когда он будет впервые использован.
DataImporter
промаркирован как lazy
, то свойство importer
будет создано только тогда, когда оно впервые будет использовано, например когда будет запрошена его свойство filename
:
print(manager.importer.filename)
// объект класса DataImporter для свойства importer теперь был создан
// Выведет "data.txt"
Rect
также предоставляет вычисляемое свойство с названием center
. Текущая центральная позиция Rect
всегда может быть определена из его origin
и size
, так что Вам не нужно хранить центральную точку в качестве явного значения Point
. Вместо этого Rect
определяет кастомные геттер и сеттер для вычисляемого свойства с названием center
, чтобы позволит Вам работать с center
прямоугольника, как если бы это было настоящее хранимое свойство.
square
и типом Rect
. Переменная square
инициализируется началом отсчёта в виде точки (0, 0) с длиной и шириной 10. Эта фигура представлена голубым квадратом на диаграмме ниже.
center
переменной square
затем вызывается с помощью точечного синтаксиса (square.center)
, что заставляет геттер для center
быть вызванным для получения текущего значения свойства. Вместо возвращения существующего значения геттер фактически вычисляет и возвращает новую Point
для представления центра квадрата. Как это можно увидеть выше, геттер корректно возвращает центральную точку (5, 5).
center
затем устанавливается в новое значение (15, 15), что смещает квадрат вверх и вправо в новую позицию показанную оранжевым квадратом на диаграмме ниже. Установка свойства center
вызывает сеттер для center
, что изменяет значения x и y для хранимого свойства origin
и смещает квадрат на новую позицию.
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Выводит "square.origin is now at (10.0, 10.0)"
newValue
. Здесь представлена альтернативная версия структуры Rect
, которая использует преимущества этой краткой нотации:
struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
var
, так как их значением не фиксировано. Ключевое слово let
используется только для константных свойств, чтобы указать, что их значение не может быть изменено, как только они будут установлены в процессе инициализации объекта.
get
из их фигурных скобок:
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// Выведет "the volume of fourByFiveByTwo is 40.0"
Этот пример определяет новую структуру с названием Cuboid
, что репрезентует трёхмерный прямоугольный ящик со свойствами width
, height
и depth
. Эта структура также имеет вычисляемое свойство только-для-чтения с названием volume
, которое вычисляет и возвращает текущий объем кубоида. Нет смысла делать объём задаваемым, так как может быть неопределено, какие значения width
, height
и depth
должны использовать для конкретного значения volume
. Несмотря на это, это полезно для Cuboid
предоставить вычисляемое свойство только-для-чтения для дачи возможности внешним пользователям узнать его текущий объём.
willSet
, то он получит новое свойство в качестве константного параметра. Вы можете определить имя для этого параметра как часть Вашей реализации willSet
. Если Вы не напишите имя параметра и круглые скобки в Вашей реализации, то параметр станет доступен с именем по-умолчанию - newValue
.
didSet
, то он получить константный параметр со значением старого свойства. Вы можете дать ему имя или использовать стандартное oldValue
. Если Вы присвоите значение свойству из его собственного наблюдателя didSet
, новое значение, которое Вы присвоите, заменит то, которое Вы только что задали.
willSet
и didSet
в действии. Определим новый класс с названием StepCounter
, который отслеживает общее количество шагов, которые человек делает в течение прогулки. Этот класс может быть использован со входными данными из педометра или другого отслеживателя шагов:
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
func someFunction(_ x: inout Int) {
x = 134
}
someFunction(&stepCounter.totalSteps)
Класс stepCounter
определяет свойство totalSteps
типа Int
. Это хранимое свойство с наблюдателями willSet
и didSet
.
willSet
и didSet
для totalSteps
вызываются всякий раз, когда свойству присваивается новое значение.
willSet
использует имя параметра для newTotalSteps
для нового значения. В этом примере он просто выводит то значение, которое готовится к установке.
didSet
вызывается после того, как значение totalSteps
обновлено. Он сравнивает новое значение totalSteps
против старого. Если общее количество шагов возросло, то сообщение печатается для индикации того, как много шагов было сделано. Наблюдатель didSet
не предоставляет специального имени параметра для старого значение, так что вместо него используется имя по-умолчанию - oldValue
.
willSet
и didSet
всегда вызываются. Это работает ввиду наличия модели копировать-во-внутрь/копировать-из памяти для in-out параметров: значение всегда записывается назад в свойство в конце выполнения функции.
lazy
.
lazy
.
static
. Пример ниже демонстрирует синтаксис для хранимого и вычисляемого свойств типа:
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
Вычисляемое свойство типа в примере доступно только-для-чтения, но Вы также можете задать перезаписываемое вычисляемое свойство с тем же синтаксисом.
print(SomeStructure.storedTypeProperty)
// Выводит "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Выводит "Another value."
print(SomeEnumeration.computedTypeProperty)
// Выводит "6"
print(SomeClass.computedTypeProperty)
// Выводит "27»
AudioChannel
:
struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.thresholdLevel {
// поднять значение чего-то там до чего-то там
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// сохранить это значение в качестве нового максимального входного уровня
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}
Структура AudioChannel
определяет два хранимых свойства типа для поддержуи своей функциональности. Первое thresholdLevel
определяет максимальное значение threshold
, которое может принять уровень звука. Это константное значение 10 для всех экземпляров AudioChannel
. Если аудиосигнал идёт со значением выше 10, то он будет обрезан до значения threshold
(как описано ниже).
maxInputLevelForAllChannels
. Оно отслеживает максимальное входное значение, которое было получено любым объектом типа AudioChannel
. Оно задаётся изначально в 0.
AudioChannel
также определяет хранимое свойство экземпляра с названием currentLevel
, которое отображает текущее значение уровня звука по шкале от 0 до 10.
currentLevel
имеет наблюдателя свойства didSet
для проверки того, какое значение currentLevel
установлено. Этот наблюдатель выполняет две проверки:
currentLevel
больше чем допустимое thresholdLevel
, то наблюдатель свойства обрежет currentLevel
до значения thresholdLevel
.
Если новое значение currentLevel
(после всех обрезок) выше любого значения, полученного ранее любым объектом типа AudioChannel
, то обозреватель свойства сохранит новое значение currentLevel
в свойстве типа maxInputLevelForAllChannels
.
didSet
устанавливает значение currentLevel
в другое значение. Это однако не вызывает повторное срабатывание наблюдателя.
AudioChannel
для создания двух новых аудиоканалов с названиями leftChannel
и rightChannel
для представления уровней звука в стереаудиосистеме:
var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
currentLevel
для левого канала в 7, то Вы можете увидеть, что maxInputLevelForAllChannels
свойство типа теперь равно 7.
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Выведет "7"
print(AudioChannel.maxInputLevelForAllChannels)
// Выведет "7"
currentLevel
правого канала в 11, то Вы увидите, что свойство currentLevel
правого канала погашено максимальным значением 10 и maxInputLevelForAllChannels
обновлено до 10:
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Выведет "10"
print(AudioChannel.maxInputLevelForAllChannels)
// Выведет "10"