Свойства
Свойства связывают значения с определенным классом, структурой или перечислением. Сохраненные свойства хранят постоянные значения и значения переменных как часть экземпляра, тогда как вычисленные свойства вычисляют (а не хранилище) значение. Вычисленные свойства предоставлены классами, структурами и перечислениями. Сохраненные свойства предоставлены только классами и структурами.
Сохраненные и вычисленные свойства обычно связываются с экземплярами определенного типа. Однако свойства могут также быть связаны с самим типом. Такие свойства известны как свойства типа.
Кроме того, можно определить наблюдателей свойства для наблюдения изменений в значении свойства, на которое можно ответить с пользовательскими действиями. Наблюдатели свойства могут быть добавлены к сохраненным свойствам, которые Вы определяете сами, и также к свойствам, которые подкласс наследовал от его суперкласса.
Сохраненные свойства
В его самой простой форме сохраненное свойство является константой или переменный, который сохранен как часть экземпляра определенного класса или структуры. Сохраненные свойства могут быть любой переменными сохраненными свойствами (представленный var
ключевое слово) или постоянные сохраненные свойства (представленный let
ключевое слово).
Можно обеспечить значение по умолчанию для сохраненного свойства как часть его определения, как описано в Значениях свойств По умолчанию. Можно также установить и изменить начальное значение для сохраненного свойства во время инициализации. Это - истина даже для постоянных сохраненных свойств, как описано в Присвоении Постоянных Свойств Во время Инициализации.
Пример ниже определяет вызванную структуру FixedLengthRange
, который описывает диапазон целых чисел, длина диапазона которых не может быть изменена, как только она создается:
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8
Экземпляры FixedLengthRange
имейте сохраненное вызванное свойство переменной firstValue
и вызывают постоянное сохраненное свойство length
. В примере выше, length
инициализируется, когда новый диапазон создается и не может быть изменен после того, потому что это - постоянное свойство.
Сохраненные свойства постоянных экземпляров структуры
Если Вы создаете экземпляр структуры и присваиваете тот экземпляр константе, Вы не можете изменить свойства экземпляра, даже если они были объявлены как переменные свойства:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property
Поскольку rangeOfFourItems
объявляется как константа (с let
ключевое слово), не возможно изменить firstValue
свойство, даже при том, что firstValue
переменное свойство.
Это поведение вследствие структур, являющихся типами значения. Когда экземпляр типа значения отмечен как константа, так все ее свойства.
То же не является истиной для классов, которые являются ссылочными типами. При присвоении экземпляра ссылочного типа к константе можно все еще изменить переменные свойства того экземпляра.
Ленивые сохраненные свойства
Ленивое сохраненное свойство является свойством, начальное значение которого не вычисляется до первого раза это используется. Вы указываете ленивое сохраненное свойство путем записи lazy
модификатор перед его объявлением.
Ленивые свойства полезны, когда начальное значение для свойства зависит от внешних факторов, значения которых не известны, пока инициализация экземпляра не завершена. Ленивые свойства также полезны, когда начальное значение для свойства требует комплекса или в вычислительном отношении дорогой установки, которая не должна быть выполнена, если или пока это не необходимо.
Пример ниже использует ленивое сохраненное свойство для предотвращения ненужной инициализации сложного класса. Этот пример определяет два вызванные класса DataImporter
и DataManager
, ни один из которых не показан полностью:
class DataImporter {
/*
DataImporter is a class to import data from an external file.
The class is assumed to take a non-trivial amount of time to initialize.
*/
var fileName = "data.txt"
// the DataImporter class would provide data importing functionality here
}
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
// the DataManager class would provide data management functionality here
}
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// the DataImporter instance for the importer property has not yet been created
DataManager
классу вызвали сохраненное свойство data
, который инициализируется с новым, пустым массивом String
значения. Несмотря на то, что остальная часть ее функциональности не показана, цель этого DataManager
класс должен управлять и обеспечить доступ к этому массиву String
данные.
Часть функциональности DataManager
класс является возможностью импортировать данные из файла. Эта функциональность предоставлена DataImporter
класс, который, как предполагается, занимает нетривиальное количество времени для инициализации. Это могло бы быть то, потому что a DataImporter
экземпляр должен открыть файл и считать его содержание в память когда DataImporter
экземпляр инициализируется.
Это возможно для a DataManager
экземпляр для управления его данными, никогда не импортируя данные из файла, таким образом, нет никакой потребности создать новое DataImporter
экземпляр, когда DataManager
самостоятельно создается. Вместо этого имеет больше смысла создавать DataImporter
экземпляр, если и когда это сначала используется.
Поскольку это отмечено с lazy
модификатор, DataImporter
экземпляр для importer
свойство только создается когда importer
к свойству сначала получают доступ, такой как тогда, когда fileName
свойство запрашивается:
println(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// prints "data.txt"
Сохраненные свойства и переменные экземпляра
Если у Вас есть опыт с Objective C, можно знать, что это обеспечивает два способа сохранить значения и ссылки как часть экземпляра класса. В дополнение к свойствам можно использовать переменные экземпляра в качестве запоминающего устройства для значений, сохраненных в свойстве.
Swift объединяет эти понятия в единственное объявление свойства. Свойство Swift не имеет соответствующей переменной экземпляра, и к запоминающему устройству для свойства не получают доступ непосредственно. Этот подход избегает беспорядка о том, как к значению получают доступ в различных контекстах и упрощает объявление свойства в единственный, категорический оператор. Вся информация о свойстве — включая его имя, введите, и характеристики управления памятью — определяются в единственном расположении как часть определения типа.
Вычисленные свойства
В дополнение к сохраненным свойствам классы, структуры и перечисления могут определить вычисленные свойства, фактически не хранящие значение. Вместо этого они предоставляют методу get и дополнительному методу set, чтобы получить и установить другие свойства и значения косвенно.
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)
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// prints "square.origin is now at (10.0, 10.0)"
Этот пример определяет три структуры для работы с геометрическими фигурами:
Point
инкапсулирует(x, y)
координата.Size
инкапсулирует awidth
и aheight
.Rect
определяет прямоугольник точкой источника и размером.
Rect
структура также обеспечивает вычисленное вызванное свойство center
. Текущая центральная позиция a Rect
может всегда определяться от origin
и size
, и таким образом, Вы не должны хранить центральную точку как явное Point
значение. Вместо этого Rect
определяет пользовательского метода get и метод set для вычисленной вызванной переменной center
, позволять Вам работать с прямоугольником center
как будто это было реальное сохраненное свойство.
Предыдущий пример создает новое Rect
переменную вызывают square
. square
переменная инициализируется с точкой источника (0, 0)
, и ширина и высота 10
. Этот квадрат представлен синим квадратом в схеме ниже.
square
переменная center
к свойству тогда получают доступ через точечный синтаксис (square.center
), который вызывает метода get для center
быть вызванным, получать текущее значение свойства. Вместо того, чтобы возвращать существующее значение, метод get фактически вычисляет и возвращает новое Point
представлять центр квадрата. Как видно вышеупомянутого метод get правильно возвращает центральную точку (5, 5)
.
center
свойство тогда установлено в новое значение (15, 15)
, который повышает квадрат и вправо к новой позиции, показанной оранжевым квадратом в схеме ниже. Установка center
свойство вызывает метод set для center
, который изменяет x
и y
значения сохраненного origin
свойство и перемещения квадрат к его новой позиции.
Краткое объявление метода set
Если метод set вычисленного свойства не определяет имя для нового значения, которое будет установлено, имя по умолчанию 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)
}
}
}
Вычисленные свойства только для чтения
Вычисленное свойство с методом get, но никаким методом set известно как вычисленное свойство только для чтения. Вычисленное свойство только для чтения всегда возвращает значение, и может быть получено доступ через точечный синтаксис, но не может быть установлено в различное значение.
Можно упростить объявление вычисленного свойства только для чтения путем удаления 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)
println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// prints "the volume of fourByFiveByTwo is 40.0"
Этот пример определяет новую вызванную структуру Cuboid
, который представляет 3D прямоугольник с width
, height
, и depth
свойства. Эта структура также имеет вычисленное вызванное свойство только для чтения volume
, который вычисляет и возвращает текущий объем кубоида. Это не целесообразно для volume
быть устанавливаемым, потому что это было бы неоднозначно относительно который значения width
, height
, и depth
должен использоваться для детали volume
значение. Тем не менее, это полезно для a Cuboid
обеспечить вычисленное свойство только для чтения, чтобы позволить внешним пользователям обнаружить его текущий расчетный объем.
Наблюдатели свойства
Наблюдатели свойства наблюдают и реагируют на изменения в значении свойства. Наблюдателей свойства вызывают каждый раз, когда значение свойства установлено, даже если новое значение совпадает с текущей стоимостью свойства.
Можно добавить наблюдателей свойства к любым сохраненным свойствам, которые Вы определяете кроме ленивых сохраненных свойств. Можно также добавить наблюдателей свойства к любому унаследованному свойству (или сохраненный или вычисленный) путем переопределения свойства в подклассе. Переопределение свойства описано в Переопределении.
У Вас есть опция определить или или оба из этих наблюдателей на свойстве:
willSet
вызывается непосредственно перед тем, как значение сохранено.didSet
сразу вызывается после того, как новое значение сохранено.
Если Вы реализуете a willSet
наблюдатель, это передается новое значение свойства как постоянный параметр. Можно указать имя для этого параметра как часть Вашего willSet
реализация. Если Вы примете решение не записать название параметра и круглые скобки в Вашей реализации, то параметр будет все еще сделан доступным с названием параметра по умолчанию newValue
.
Точно так же, если Вы реализуете a didSet
наблюдатель, это будет передано постоянный параметр, содержащий старое значение свойства. Если Вы желаете или используете название параметра по умолчанию, можно назвать параметр oldValue
.
Вот пример willSet
и didSet
в действии. Пример ниже определяет новый вызванный класс StepCounter
, который отслеживает общее количество шагов, которые лицо предпринимает при обходе. Этот класс мог бы использоваться с входными данными от шагомера, или другой шаг в противоречии с отслеживают осуществление лица во время их распорядка дня.
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
println("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
println("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
StepCounter
класс объявляет a totalSteps
свойство типа Int
. Это - сохраненное свойство с willSet
и didSet
наблюдатели.
willSet
и didSet
наблюдатели для totalSteps
вызываются каждый раз, когда свойство присваивается новое значение. Даже если новое значение совпадает с текущей стоимостью, это - истина.
Этот пример willSet
наблюдатель использует пользовательское название параметра newTotalSteps
для предстоящего нового значения. В этом примере это просто распечатывает значение, собирающееся быть установленным.
didSet
наблюдателя вызывают после значения totalSteps
обновляется. Это сравнивает новое значение totalSteps
против старого значения. Если общее количество шагов увеличилось, сообщение распечатано для указания, сколько новых шагов было предпринято. didSet
наблюдатель не обеспечивает пользовательское название параметра для старого значения и имя по умолчанию oldValue
используется вместо этого.
Глобальные и локальные переменные
Возможности, описанные выше для вычислений и наблюдения свойств, также доступны глобальным переменным и локальным переменным. Глобальные переменные являются переменными, определяющимися за пределами любой функции, метода, закрытия, или вводящими контекст. Локальные переменные являются переменными, определяющимися в функции, методе или контексте закрытия.
Глобальные и локальные переменные, с которыми Вы встретились в предыдущих главах, все были сохраненными переменными. Сохраненные переменные, как сохраненные свойства, обеспечивают хранение для значения определенного типа и позволяют тому значению быть установленным и полученным.
Однако можно также определить вычисленные переменные и определить наблюдателей для сохраненных переменных, или в глобальном или в локальном объеме. Вычисленные переменные вычисляют, а не хранят значение и записаны таким же образом как вычисленные свойства.
Введите свойства
Свойства экземпляра являются свойствами, принадлежащими экземпляру определенного типа. Каждый раз, когда Вы создаете новый экземпляр того типа, это имеет свой собственный набор значений свойств, отдельных от любого другого экземпляра.
Можно также определить свойства, принадлежащие самому типу, не любому экземпляру того типа. Только когда-либо будет одна копия этих свойств, независимо от того сколько экземпляров того типа Вы создаете. Эти виды свойств вызывают свойствами типа.
Свойства типа полезны для определения значений, которые универсальны ко всем экземплярам определенного типа, таковы как постоянное свойство, которое могут использовать все экземпляры (как статическая константа в C), или переменное свойство, хранящее значение, которое является глобальной переменной ко всем экземплярам того типа (как статическая переменная в C).
Для типов значения (т.е. структуры и перечисления), можно определить сохраненные и вычисленные свойства типа. Для классов можно определить вычисленные свойства типа только.
Сохраненные свойства типа для типов значения могут быть переменными или константами. Вычисленные свойства типа всегда объявляются как переменные свойства, таким же образом как вычисленные свойства экземпляра.
Введите синтаксис свойства
В C и Objective C, Вы определяете статические константы и переменные, связанные с типом как глобальные статические переменные. В Swift, однако, свойства типа записаны как часть определения типа во внешних изогнутых фигурных скобках типа, и каждое свойство типа явно ограничено по объему к типу, который это поддерживает.
Вы определяете свойства типа с помощью static
ключевое слово. Для вычисленных свойств типа для типов классов можно использовать class
ключевое слово вместо этого, чтобы позволить подклассам переопределять реализацию суперкласса. Пример ниже шоу синтаксис для сохраненных и вычисленных свойств типа:
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// return an Int value here
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// return an Int value here
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// return an Int value here
}
class var overrideableComputedTypeProperty: Int {
// return an Int value here
}
}
Запросы и установка свойств типа
Введите свойства, запрашиваются и устанавливаются с точечным синтаксисом, точно так же, как свойства экземпляра. Однако введите свойства, запрашиваются и устанавливаются на типе, не на экземпляре того типа. Например:
println(SomeClass.computedTypeProperty)
// prints "42"
println(SomeStructure.storedTypeProperty)
// prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
println(SomeStructure.storedTypeProperty)
// prints "Another value."
Примеры, следующие за использованием два сохраненных свойства типа как часть структуры, моделирующей метр уровня звука для многих звуковых каналов. Каждый канал имеет целочисленный уровень звука между 0
и 10
включительно.
Число ниже иллюстрирует, как два из этих звуковых каналов могут быть объединены для моделирования метра уровня звука стерео. Когда уровень звука канала 0
, ни одни из световых сигналов для того канала не освещены. Когда уровень звука 10
, все световые сигналы для того канала освещены. В этом числе левый канал имеет текущий уровень 9
, и правильный канал имеет текущий уровень 7
:
Звуковые каналы, описанные выше, представлены экземплярами AudioChannel
структура:
struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.thresholdLevel {
// cap the new audio level to the threshold level
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// store this as the new overall maximum input level
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}
AudioChannel
структура определяет два сохраненных свойства типа для поддержки его функциональности. Первое, thresholdLevel
, определяет максимальное пороговое значение, которое может взять уровень звука. Это - постоянное значение 10
для всех AudioChannel
экземпляры. Если звуковой сигнал входит с более высоким значением, чем 10
, это будет ограничено к этому пороговому значению (как описано ниже).
Второе свойство типа является сохраненным вызванным свойством переменной maxInputLevelForAllChannels
. Это отслеживает максимальное входное значение, полученное любым AudioChannel
экземпляр. Это запускается с начального значения 0
.
AudioChannel
структура также определяет сохраненное вызванное свойство экземпляра currentLevel
, который представляет текущий уровень звука канала в масштабе 0
к 10
.
currentLevel
свойство имеет a didSet
наблюдатель свойства для проверки значения currentLevel
каждый раз, когда это установлено. Этот наблюдатель выполняет две проверки:
Если новое значение
currentLevel
больше, чем позволенныйthresholdLevel
, прописные буквы наблюдателя свойстваcurrentLevel
кthresholdLevel
.Если новое значение
currentLevel
(после любого ограничения), выше, чем какое-либо значение, ранее полученное любымAudioChannel
экземпляр, наблюдатель свойства хранит новоеcurrentLevel
значение вmaxInputLevelForAllChannels
введите свойство.
Можно использовать AudioChannel
структура для создания двух новых вызванных звуковых каналов leftChannel
и rightChannel
, представлять уровни звука аудиосистемы стерео:
var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
Если Вы устанавливаете currentLevel
из левого канала к 7
, Вы видите что maxInputLevelForAllChannels
свойство типа обновляется для равенства 7
:
leftChannel.currentLevel = 7
println(leftChannel.currentLevel)
// prints "7"
println(AudioChannel.maxInputLevelForAllChannels)
// prints "7"
При попытке установить currentLevel
из правильного канала к 11
, Вы видите что правильный канал currentLevel
свойство ограничивается к максимальному значению 10
, и maxInputLevelForAllChannels
свойство типа обновляется для равенства 10
:
rightChannel.currentLevel = 11
println(rightChannel.currentLevel)
// prints "10"
println(AudioChannel.maxInputLevelForAllChannels)
// prints "10"