int (init?)
.
return nil
внутри проваливающегося инициализатора для указания на то, где провал инициализации может сработать.
self
полностью и корректно инициализирован к моменту окончания инициализации. Несмотря на это Вы можете написать return nil
, чтобы вызвать провал инициализации, однако Вы не используете ключевое слово return
для указания на удачную инициализацию.
init(exactly:)
. Если преобразование типов не может полностью преобразовать значение, то инициализация проваливается.
let wholeNumber: Double = 12345.0
let pi = 3.14159
if let valueMaintained = Int(exactly: wholeNumber) {
print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}
// Выведет "12345.0 conversion to Int maintains value of 12345"
let valueChanged = Int(exactly: pi)
// valueChanged имеет тип Int?, а не Int
if valueChanged == nil {
print("\(pi) conversion to Int does not maintain value")
}
// Выведет "3.14159 conversion to Int does not maintain value"
Animal
с константным свойством species
типа String
. Структура Animal
также определяет проваливающийся инициализатор с единственным параметром с названием species
. Инициализатор проверяет, если значение species
передаётся в инициализатор в качестве пустой строки. Если найдена пустая строка, то сработает провал инициализации. В противном случае будет задано значения свойства species
, а инициализация удастся:
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
Animal
и проверить, удачна ли прошла инициализация:
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// Выведет "An animal was initialized with a species of Giraffe"
species
, то инициализатор выдаст ошибку инициализации:
let anonymousCreature = Animal(species: "")
// anonymousCreature имеет тип Animal?, а не Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// Выведет "The anonymous creature could not be initialized"
Проверка на пустое строковое значение (как например "" вместо "Giraffe") - это не то же самое, что проверка на наличие nil
для индикаии на отсутствие значения у объекта типа String-опционал. В примере выше пустая строка ("") - это корректная неопциональная строка. Однако недопустимо для животного иметь пустую строку в качестве значения species
. Для моделирования этого ограничения проваливающийся конструктор вызывает ошибку инициализации, если получена пустая строка
TemperatureUnit
c тремя возможными состояниями (kelvin, celsius и fahrenheit). Проваливающийся инициализатор используется для нахождения подходящего кейса перечисления для значения типа Character
, представляющего символ температуры:
enum TemperatureUnit {
case kelvin, celsius, fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .kelvin
case "C":
self = .celsius
case "F":
self = .fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Выведет "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Выведет "This is not a defined temperature unit, so initialization failed."
init?(rawValue:)
, который принимает параметр с названием rawValue
подходящего типа для чистого значения и выбирает подходящий кейс перечисления, если он найден, или вызывает провал инициализации, если совпадающих значений не существует.
TemperatureUnit
выше для использования чистых значений типа Character
и для получения преимущества от инициализатора init?(rawValue:)
:
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Выведет "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Выведет "This is not a defined temperature unit, so initialization failed."
Product
с названием CartItem
. Класс CartItem
моделирует вещь в онлайн продуктовой тележке. CartItem
представляет хранимое свойство постоянной квалификации с названием quantity
и гарантирует, что оно всегда будет иметь значение по крайней мере 1:
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
Проваливающийся конструктор для CartItem
начинает работу с проверки того, что он получил значение quantity
не менее 1. Если значение quantity
некорректно, то весь процесс инициализации немедленно проваливается и никакой дальнейший код инициализации не будет выполнен. Подобно этому проваливающийся конструктор для Product
проверяет значение name
, а процесс инициализации немедленно провяливается, если name
- это пустая строка.
CartItem
с негустым именем и количеством 1 или более, то инициализация будет успешной:
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// Выведет "Item: sock, quantity: 2"
CartItem
со значением свойства quantity
, равным 0, то инициализатор CartItem
вызовет провал:
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// Выведет "Unable to initialize zero shirts"
CartItem
c пустым значением name
, то инициализатор надкласса Product
вызовет провал инициализации:
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// Выведет "Unable to initialize one unnamed product"
Document
. Это класс моделирует документ, который инициализируется с помощью свойства name
, который может быть непустой строкой или nil
, но пустой строкой быть не может:
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
Document
c названием AutomaticallyNamedDocument
. Подкласс AutomaticallyNamedDocument
перезаписывает оба основных конструктора, определённых в Document
. Эти перезаписи гарантируют, что AutomaticallyNamedDocument
получит изначальное значение name
, равное "[Untitled]", если объект будет задан без имени или если в конструктор init(name:)
будет передана пустая строка:
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
AutomaticallyNamedDocument
перезаписывает проваливающийся инициализатор своего надкласса init?(name:)
непроваливающимся инициализатором init(name:)
. Так как AutomaticallyNamedDocument
взаимодействует с пустой строкой другим способ нежели его надкласс, то его инициализатору не должен проваливаться, а значит он предоставляет непроваливающуюся версию этого конструктора.
UntitledDocument
подкласс ниже всегда именуется "[Untitled]", и он использует проваливающийся инициализатор init?(name:)
из его надкласса в процессе инициализации.
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
В этом случае, если бы конструктор init(name:)
надкласса вызывался с пустой строкой в качестве имени, а операция принудительной распаковки превратить результат в ошибку рантайма. Однако, так как он вызван со строковой константой, Вы можете увидеть, что инициализатор не провалится, а значит не появится и никакой ошибки времени выполнения.
init (init?)
. С другой стороны, Вы можете определить проваливающийся инициализатор, который создаёт неявно извлекаемый опционал нужного типа. Вы можете сделать это, поставив восклицательный знак после ключевого слова init (init!)
вместо знака вопроса.
init?
в init!
или наоборот, а также Вы можете перезаписывать init?
с помощью init!
или наоборот. Вы также можете делегировать из init
в init!
, хотя это и может вызвать срабатывание оператора выполнения, если конструктор init!
вызовет провал инициализации.
required
перед определением инициализатора класса для указания на то, что каждый подкласс этого класса должен реализовать этот инициализатор:
class SomeClass {
required init() {
// initializer implementation goes here
}
}
required
перед каждой реализацией этого инициализатора в подклассах, чтобы обозначить то, что инициализатор должен быть реализован во всех подклассах в цепочке. Вам не нужно писать модификатор override
при переписывании требуемого основного инициализатора:
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
Заметьте, что закрывающая фигурная скобка замыкания сопровождается пустой парой круглых скобок. Это говорит Swift исполнить замыкание немедленно. Если Вы опустите эти круглые скобки, то тем самым Вы попытаетесь присвоить замыкание самому свойству, а не его возвращаемое значение.
self
или вызывать любые методы объекта.
Chessboard
, которая моделирует доску для игры в шахматы. Шахматы играются на доске 8 x 8 с чередующимися чёрными и белыми квадратами.
Chessboard
имеет единственное свойство с названием boardColors
, которое является массивом из 64 Bool
значений. Значение true
в массиве представляет собой чёрный квадрат, а значение false
- белый. Первый элемент в массиве представляет верхний левый квадрат доски, а последний - нижний правый.
boardColors
инициализируется с помощью замыкания для задания его цветовых значений:
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}
Chessboard
, то выполняется замыкание и изначальное значение boardColors
вычисляется и возвращается. Замыкание в примере выше вычисляет и задаёт подходящий цвет для каждого квадрата на игровой доске во временном массиве с названием temporaryBoard
, а затем возвращает этот временный массив в качестве возвращаемого значение замыкания, когда его выполнение заканчивается. Этот массив сохраняется в boardColors
и может быть запрошен с помощью функции-инструмента squareIsBlackAt(row:column:)
:
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Выведет "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Выведет "false"