Классы и структуры
Классы и структуры являются гибкими конструкциями общего назначения, становящимися стандартными блоками кода Вашей программы. Вы определяете свойства и методы для добавления функциональности к классам и структурам при помощи точно того же синтаксиса что касается констант, переменных и функций.
В отличие от других языков программирования, Swift не требует, чтобы Вы создали отдельный интерфейс и файлы реализации для пользовательских классов и структур. В Swift Вы определяете класс или структуру в единственном файле, и внешний интерфейс к тому классу или структуре автоматически сделан доступным для другого кода для использования.
Сравнение классов и структур
Классы и структуры в Swift имеют много общих черт. Оба могут:
Определите свойства для хранения значений
Определите методы для обеспечения функциональности
Определите нижние индексы для обеспечения доступа к их значениям с помощью нижнего синтаксиса
Определите инициализаторы для установки их начального состояния
Будьте расширены для расширения их функциональности вне реализации по умолчанию
Соответствуйте протоколам для обеспечения стандартной функциональности определенного вида
Для получения дополнительной информации посмотрите Свойства, Методы, Нижние индексы, Инициализацию, Расширения и Протоколы.
Классы имеют дополнительные возможности, которые не делают структуры:
Наследование позволяет одному классу наследовать характеристики другого.
Преобразование типа позволяет Вам проверить и интерпретировать тип экземпляра класса во время выполнения.
Deinitializers позволяют экземпляру класса высвободить любые ресурсы, которые это присвоило.
Подсчет ссылок позволяет больше чем одну ссылку на экземпляр класса.
Для получения дополнительной информации посмотрите Наследование, Преобразование типа, Deinitialization и Автоматический Подсчет ссылок.
Синтаксис определения
Классы и структуры имеют подобный синтаксис определения. Вы представляете классы с class
ключевое слово и структуры с struct
ключевое слово. Оба помещают их все определение в паре фигурных скобок:
class SomeClass {
// class definition goes here
}
struct SomeStructure {
// structure definition goes here
}
Вот пример определения структуры и определения класса:
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
Пример выше определяет новую вызванную структуру Resolution
, описать основанное на пикселе разрешение дисплея. Эта структура имеет два сохраненных вызванные свойства width
и height
. Сохраненные свойства являются константами или переменными, которые укутаны и сохранены как часть класса или структуры. Эти два свойства выведены, чтобы иметь тип Int
путем установки их в начальное целочисленное значение 0
.
Пример выше также определяет новый вызванный класс VideoMode
, описать определенный режим видео для видеодисплея. Этот класс имеет сохраненные свойства четырех переменных. Первое, resolution
, инициализируется с новым Resolution
экземпляр структуры, выводящий тип свойства Resolution
. Для других трех свойств, новых VideoMode
экземпляры будут инициализированы с interlaced
установка false
(значение “нечередуемого видео”), частота кадров воспроизведения 0.0
, и дополнительное String
значение вызывают name
. name
свойству автоматически дают значение по умолчанию nil
, или “нет name
оцените”, потому что это имеет дополнительный тип.
Класс и экземпляры структуры
Resolution
определение структуры и VideoMode
определение класса только описывает что a Resolution
или VideoMode
будет похож. Они сами не описывают определенное разрешение или режим видео. Чтобы сделать это, необходимо создать экземпляр структуры или класса.
Синтаксис для создания экземпляров очень подобен и для структур и для классов:
let someResolution = Resolution()
let someVideoMode = VideoMode()
Структуры и классы оба синтаксиса инициализатора использования для новых экземпляров. Самая простая форма синтаксиса инициализатора использует имя типа класса или структуры, сопровождаемой пустыми круглыми скобками, такой как Resolution()
или VideoMode()
. Это создает новый экземпляр класса или структуры с любыми свойствами, инициализированными к их значениям по умолчанию. Инициализация класса и структуры описана более подробно в Инициализации.
Доступ к свойствам
Можно получить доступ к свойствам экземпляра с помощью точечного синтаксиса. В точечном синтаксисе Вы сразу написали имя свойства после имени экземпляра, разделенного периодом (.
), без любых пробелов:
println("The width of someResolution is \(someResolution.width)")
// prints "The width of someResolution is 0"
В этом примере, someResolution.width
относится к width
свойство someResolution
, и возвращает его начальное значение по умолчанию 0
.
Можно выполнить развертку в подсвойства, такой как width
свойство в resolution
свойство a VideoMode
:
println("The width of someVideoMode is \(someVideoMode.resolution.width)")
// prints "The width of someVideoMode is 0"
Можно также использовать точечный синтаксис для присвоения нового значения переменному свойству:
someVideoMode.resolution.width = 1280
println("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// prints "The width of someVideoMode is now 1280"
Инициализаторы Memberwise для типов структуры
Все структуры имеют автоматически сгенерированный memberwise инициализатор, который можно использовать для инициализации свойств элемента новых экземпляров структуры. Начальные значения для свойств нового экземпляра могут быть переданы memberwise инициализатору по имени:
let vga = Resolution(width: 640, height: 480)
В отличие от структур, экземпляры класса не получают значение по умолчанию memberwise инициализатор. Инициализаторы описаны более подробно в Инициализации.
Структуры и перечисления являются типами значения
Тип значения является типом, значение которого копируется, когда это присваивается переменному или постоянному, или когда это передается функции.
Вы фактически использовали типы значения экстенсивно всюду по предыдущим главам. Фактически, все основные типы в Swift — целые числа, числа с плавающей точкой, булевские переменные, строки, массивы и словари — являются типами значения и реализованы как структуры негласно.
Все структуры и перечисления являются типами значения в Swift. Это означает, что любая структура и экземпляры перечисления, которые Вы создаете — и любые типы значения, которые они имеют, поскольку свойства — всегда копируются, когда они розданы в Вашем коде.
Рассмотрите этот пример, использующий Resolution
структура от предыдущего примера:
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
Этот пример объявляет вызванную константу hd
и наборы это к a Resolution
экземпляр, инициализированный с шириной и высотой видео full HD (1920
пиксели, широкие 1080
пиксели высоко).
Это тогда объявляет вызванную переменную cinema
и наборы это к текущей стоимости hd
. Поскольку Resolution
структура, копия существующего экземпляра сделана, и эта новая копия присваивается cinema
. Даже при том, что hd
и cinema
теперь имейте ту же ширину и высоту, они - два абсолютно различных экземпляра негласно.
Затем, width
свойство cinema
исправляется, чтобы быть шириной немного более широкого 2K стандарта, используемого для цифровой проекции кино (2048
широкие пиксели и 1080
пиксели высоко):
cinema.width = 2048
Проверка width
свойство cinema
показывает, что это действительно изменилось, чтобы быть 2048
:
println("cinema is now \(cinema.width) pixels wide")
// prints "cinema is now 2048 pixels wide"
Однако width
свойство оригинала hd
экземпляр все еще имеет старое значение 1920
:
println("hd is still \(hd.width) pixels wide")
// prints "hd is still 1920 pixels wide"
Когда cinema
был дан текущую стоимость hd
, значения, сохраненные в hd
были скопированы в новое cinema
экземпляр. Конечный результат является двумя абсолютно отдельными экземплярами, которые просто, оказалось, содержали те же числовые значения. Поскольку они - отдельные экземпляры, устанавливая ширину cinema
к 2048
не влияет на ширину, сохраненную в hd
.
То же поведение применяется к перечислениям:
enum CompassPoint {
case North, South, East, West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberedDirection == .West {
println("The remembered direction is still .West")
}
// prints "The remembered direction is still .West"
Когда rememberedDirection
присваивается значение currentDirection
, это фактически установлено в копию того значения. Изменение значения currentDirection
после того не влияет на копию исходного значения, которое было сохранено в rememberedDirection
.
Классы являются ссылочными типами
В отличие от типов значения, не копируются ссылочные типы, когда они присваиваются переменному или постоянному, или когда они передаются функции. Вместо копии, ссылка на тот же существующий экземпляр используется вместо этого.
Вот пример, с помощью VideoMode
класс, определенный выше:
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
Этот пример объявляет новую вызванную константу tenEighty
и наборы это для обращения к новому экземпляру VideoMode
класс. Режим видео присваивается копия разрешения HD 1920
1080
до. Это установлено быть чередованным и дано имя "1080i"
. Наконец, это установлено в частоту кадров 25.0
кадры в секунду.
Затем, tenEighty
присваивается новой константе, вызванной alsoTenEighty
, и частота кадров alsoTenEighty
изменяется:
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
Поскольку классы являются ссылочными типами, tenEighty
и alsoTenEighty
фактически оба обращаются к тому же VideoMode
экземпляр. Эффективно, они - всего два различных имени для того же единственного экземпляра.
Проверка frameRate
свойство tenEighty
показывает, что это правильно сообщает о новой частоте кадров 30.0
от базового VideoMode
экземпляр:
println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// prints "The frameRate property of tenEighty is now 30.0"
Обратите внимание на то, что tenEighty
и alsoTenEighty
объявляются как константы, а не переменные. Однако можно все еще измениться tenEighty.frameRate
и alsoTenEighty.frameRate
потому что значения tenEighty
и alsoTenEighty
сами константы фактически не изменяются. tenEighty
и alsoTenEighty
самостоятельно не «храните» VideoMode
экземпляр — вместо этого, они оба обращаются к a VideoMode
экземпляр негласно. Это frameRate
свойство базового VideoMode
это изменяется, не значения постоянных ссылок на это VideoMode
.
Операторы идентификационных данных
Поскольку классы являются ссылочными типами, для многократных констант и переменных возможно относиться к тому же единственному экземпляру класса негласно. (То же не является истиной для структур и перечислений, потому что они всегда копируются, когда они присваиваются константе или переменные, или передаются функции.)
Может иногда быть полезно узнать, относятся ли две константы или переменные к точно тому же экземпляру класса. Для включения этого Swift предоставляет двум операторам идентификационных данных:
Идентичный (
===
)Не идентичный (
!==
)
Используйте этих операторов, чтобы проверить, относятся ли две константы или переменные к тому же единственному экземпляру:
if tenEighty === alsoTenEighty {
println("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
Обратите внимание на то, что “идентичный” (представленный три равняется знакам, или ===
) не означает ту же вещь как “равную” (представленный два, равняется знакам, или ==
):
“Идентичный” означает, что две константы или переменные типа класса относятся к точно тому же экземпляру класса.
“Равный” означает, что два экземпляра считают «равными» или «эквивалентными» в значении, для некоторого надлежащего значения «равных», как определено разработчиком типа.
При определении собственных классов и структур это - ответственность решить то, что квалифицирует как два экземпляра, являющиеся «равным». Процесс определения Ваших собственных реализаций “равный” и “не равный” операторам описан в Эквивалентных Операторах.
Указатели
Если у Вас есть опыт с C, C++ или Objective C, можно знать, что эти языки используют указатели для обращения к адресам в памяти. Swift, постоянный или переменный, который относится к экземпляру некоторого ссылочного типа, подобен указателю в C, но не является прямым указателем на адрес в памяти и не требует, чтобы Вы записали звездочку (*
) указать, что Вы создаете ссылку. Вместо этого эти ссылки определяются как любая другая константа или переменные в Swift.
Выбор между классами и структурами
Можно использовать и классы и структуры для определения пользовательских типов данных для использования в качестве стандартных блоков кода программы.
Однако экземпляры структуры всегда передаются значением, и экземпляры класса всегда передаются ссылкой. Это означает, что они подходят для различных видов задач. Поскольку Вы рассматриваете конструкции данных и функциональность, в которой Вы нуждаетесь для проекта, решаете, должна ли каждая конструкция данных быть определена как класс или как структура.
Как общее руководство, рассмотрите создание структуры, когда один или больше этих условий применяйтесь:
Основная цель структуры состоит в том, чтобы инкапсулировать несколько относительно простых значений данных.
Разумно ожидать, что инкапсулированные значения будут скопированы, а не сосланы, когда Вы присвоите или раздаете экземпляр той структуры.
Любые свойства, сохраненные структурой, являются самостоятельно типами значения, которые, как также ожидали бы, будут скопированы, а не сосланы.
Структура не должна наследовать свойства или поведение от другого существующего типа.
Примеры хороших кандидатов на структуры включают:
Размер геометрической фигуры, возможно инкапсулируя a
width
свойство и aheight
свойство, оба из типаDouble
.Способ относиться к диапазонам в ряду, возможно инкапсулируя a
start
свойство и alength
свойство, оба из типаInt
.Точка в 3D системе координат, возможно инкапсулируя
x
,y
иz
свойства, каждый типDouble
.
Во всех других случаях определите класс и создайте экземпляры того класса, который будет управляем и передан ссылкой. На практике это означает, что большинство пользовательских конструкций данных должно быть классами, не структурами.
Присвоение и поведение копии для строк, массивов и словарей
Swift String
, Array
, и Dictionary
типы реализованы как структуры. Это означает, что строки, массивы и словари копируются, когда они присваиваются новой константе или переменные, или когда они передаются функции или методу.
Это поведение отличается от NSString
, NSArray
, и NSDictionary
в Основе, которые реализованы как классы, не структуры. NSString
, NSArray
, и NSDictionary
экземпляры всегда присваиваются и раздаются как ссылка на существующий экземпляр, а не как копия.