mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-23 17:41:41 +00:00
698 lines
26 KiB
Markdown
698 lines
26 KiB
Markdown
---
|
||
contributors:
|
||
- ["Grant Timmerman", "http://github.com/grant"]
|
||
- ["Christopher Bess", "http://github.com/cbess"]
|
||
- ["Joey Huang", "http://github.com/kamidox"]
|
||
- ["Alexey Nazaroff", "http://github.com/rogaven"]
|
||
filename: learnswift-ru.swift
|
||
translators:
|
||
- ["Dmitry Bessonov", "https://github.com/TheDmitry"]
|
||
- ["Alexey Nazaroff", "https://github.com/rogaven"]
|
||
---
|
||
|
||
Swift - это язык программирования, созданный компанией Apple, для приложений
|
||
под iOS и macOS. Разработанный, чтобы сосуществовать с Objective-C и
|
||
быть более устойчивым к ошибочному коду, Swift был представлен в 2014 году на
|
||
конференции разработчиков Apple, WWDC. Приложения на Swift собираются
|
||
с помощью LLVM-компилятора, включенного в Xcode 6+.
|
||
|
||
Официальная книга по [языку программирования Swift](https://itunes.apple.com/us/book/swift-programming-language/id881256329) от Apple доступна в Apple Books.
|
||
|
||
Смотрите еще [начальное руководство](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/RoadMapiOS/index.html) Apple, которое содержит полное учебное пособие по Swift.
|
||
|
||
```swift
|
||
// Версия Swift: 3.0
|
||
|
||
// импорт модуля
|
||
import UIKit
|
||
|
||
//
|
||
// MARK: Основы
|
||
//
|
||
|
||
// Xcode поддерживает маркеры, чтобы давать примечания своему коду
|
||
// и вносить их в список обозревателя (Jump Bar)
|
||
// MARK: Метка раздела
|
||
// MARK: - Метка с разделителем
|
||
// TODO: Сделайте что-нибудь вскоре
|
||
// FIXME: Исправьте этот код
|
||
|
||
// Начиная со второй версии Swift, println и print объединены в методе print.
|
||
// Перенос строки теперь добавляется в конец автоматически.
|
||
print("Привет, мир!") // println – теперь просто print
|
||
print("Привет, мир!", terminator: "") // вывод текста без переноса строки
|
||
|
||
// переменные (var), значение которых можно изменить после инициализации
|
||
// константы (let), значение которых нельзя изменить после инициализации
|
||
|
||
var myVariable = 42
|
||
let øπΩ = "значение" // именование переменной символами unicode
|
||
let π = 3.1415926
|
||
let convenience = "Ключевое слово" // контекстное имя переменной
|
||
let weak = "Ключевое слово"; let override = "еще ключевое слово" // операторы
|
||
// могут быть отделены точкой с запятой
|
||
let `class` = "Ключевое слово" // обратные апострофы позволяют использовать
|
||
// ключевые слова в именовании переменных
|
||
let explicitDouble: Double = 70
|
||
let intValue = 0007 // 7
|
||
let largeIntValue = 77_000 // 77000
|
||
let label = "некоторый текст " + String(myVariable) // Приведение типа
|
||
let piText = "Pi = \(π), Pi 2 = \(π * 2)" // Вставка переменных в строку
|
||
|
||
// Сборка особых значений
|
||
// используя ключ -D сборки конфигурации
|
||
#if false
|
||
print("Не печатается")
|
||
let buildValue = 3
|
||
#else
|
||
let buildValue = 7
|
||
#endif
|
||
print("Значение сборки: \(buildValue)") // Значение сборки: 7
|
||
|
||
/*
|
||
Опционалы - это особенность языка Swift, которая допускает вам сохранять
|
||
`некоторое` или `никакое` значения.
|
||
|
||
Язык Swift требует, чтобы каждое свойство имело значение, поэтому даже nil
|
||
должен быть явно сохранен как опциональное значение.
|
||
|
||
Optional<T> является перечислением.
|
||
*/
|
||
var someOptionalString: String? = "опционал" // Может быть nil
|
||
// как и выше, только ? - это постфиксный оператор (синтаксический сахар)
|
||
var someOptionalString2: Optional<String> = "опционал"
|
||
|
||
if someOptionalString != nil {
|
||
// я не nil
|
||
if someOptionalString!.hasPrefix("opt") {
|
||
print("содержит префикс")
|
||
}
|
||
|
||
let empty = someOptionalString?.isEmpty
|
||
}
|
||
someOptionalString = nil
|
||
|
||
/*
|
||
Использование ! для доступа к несуществующему опциональному значению генерирует
|
||
рантайм ошибку. Всегда проверяйте, что опционал содержит не пустое значение,
|
||
перед тем как раскрывать его через !.
|
||
*/
|
||
|
||
// неявная развертка опциональной переменной
|
||
var unwrappedString: String! = "Ожидаемое значение."
|
||
// как и выше, только ! - постфиксный оператор (с еще одним синтаксическим сахаром)
|
||
var unwrappedString2: ImplicitlyUnwrappedOptional<String> = "Ожидаемое значение."
|
||
|
||
// If let конструкции -
|
||
// If let это специальная конструкция в Swift, которая позволяет проверить Optional
|
||
// справа от `=` непустой, и если это так - разворачивает его и присваивает левой части.
|
||
if let someOptionalStringConstant = someOptionalString {
|
||
// имеется некоторое (`Some`) значение, не `nil`
|
||
if !someOptionalStringConstant.hasPrefix("ok") {
|
||
// нет такого префикса
|
||
}
|
||
}
|
||
|
||
// Swift поддерживает сохранение значения любого типа
|
||
// Для этих целей есть два ключевых слова `Any` и `AnyObject`
|
||
// AnyObject == id
|
||
// `Any` же, в отличие от `id` в Objective-C, `Any` работает с любым значением (Class, Int, struct и т.д.)
|
||
var anyVar: Any = 7
|
||
anyVar = "Изменять значение на строку не является хорошей практикой, но возможно."
|
||
let anyObjectVar: AnyObject = Int(1) as NSNumber
|
||
|
||
/*
|
||
Комментируйте здесь
|
||
|
||
/*
|
||
Вложенные комментарии тоже поддерживаются
|
||
*/
|
||
*/
|
||
|
||
//
|
||
// MARK: Коллекции
|
||
//
|
||
|
||
/*
|
||
Массив (Array) и словарь (Dictionary) являются структурами (struct). Так
|
||
`let` и `var` также означают, что они изменяются (var) или не изменяются (let)
|
||
при объявлении переменных этих типов.
|
||
*/
|
||
|
||
// Массив
|
||
var shoppingList = ["сом", "вода", "лимоны"]
|
||
shoppingList[1] = "бутылка воды"
|
||
let emptyArray = [String]() // let == неизменный
|
||
let emptyArray2 = Array<String>() // как и выше
|
||
var emptyMutableArray = [String]() // var == изменяемый
|
||
var explicitEmptyMutableStringArray: [String] = [] // так же как и выше
|
||
|
||
|
||
// Словарь
|
||
var occupations = [
|
||
"Malcolm": "Капитан",
|
||
"kaylee": "Техник"
|
||
]
|
||
occupations["Jayne"] = "Связи с общественностью"
|
||
let emptyDictionary = [String: Float]() // let == неизменный
|
||
let emptyDictionary2 = Dictionary<String, Float>() // как и выше
|
||
var emptyMutableDictionary = [String: Float]() // var == изменяемый
|
||
var explicitEmptyMutableDictionary: [String: Float] = [:] // то же
|
||
|
||
|
||
//
|
||
// MARK: Поток управления
|
||
//
|
||
|
||
// С помощью "," можно указать дополнительные условия для раскрытия
|
||
// опциональных значений.
|
||
let someNumber = Optional<Int>(7)
|
||
if let num = someNumber, num > 3 {
|
||
print("Больше 3х")
|
||
}
|
||
|
||
// цикл for для массива
|
||
let myArray = [1, 1, 2, 3, 5]
|
||
for value in myArray {
|
||
if value == 1 {
|
||
print("Один!")
|
||
} else {
|
||
print("Не один!")
|
||
}
|
||
}
|
||
|
||
// цикл for для словаря
|
||
var dict = ["один": 1, "два": 2]
|
||
for (key, value) in dict {
|
||
print("\(key): \(value)")
|
||
}
|
||
|
||
// цикл for для диапазона чисел
|
||
for i in -1...shoppingList.count {
|
||
print(i)
|
||
}
|
||
shoppingList[1...2] = ["бифштекс", "орехи пекан"]
|
||
// используйте ..< для исключения последнего числа
|
||
|
||
// цикл while
|
||
var i = 1
|
||
while i < 1000 {
|
||
i *= 2
|
||
}
|
||
|
||
// цикл do-while
|
||
repeat {
|
||
print("привет")
|
||
} while 1 == 2
|
||
|
||
// Переключатель
|
||
// Очень мощный оператор, представляйте себе операторы `if` с синтаксическим
|
||
// сахаром
|
||
// Они поддерживают строки, объекты и примитивы (Int, Double, etc)
|
||
let vegetable = "красный перец"
|
||
switch vegetable {
|
||
case "сельдерей":
|
||
let vegetableComment = "Добавьте немного изюма, имитируя муравьев на бревнышке."
|
||
case "огурец", "кресс-салат":
|
||
let vegetableComment = "Было бы неплохо сделать бутерброд с чаем."
|
||
case let localScopeValue where localScopeValue.hasSuffix("перец"):
|
||
let vegetableComment = "Это острый \(localScopeValue)?"
|
||
default: // обязательный (чтобы предусмотреть все возможные вхождения)
|
||
let vegetableComment = "В супе все овощи вкусные."
|
||
}
|
||
|
||
|
||
//
|
||
// MARK: Функции
|
||
//
|
||
|
||
// Функции являются типом первого класса, т.е. они могут быть вложены в функциях
|
||
// и могут передаваться между собой
|
||
|
||
// Функция с документированным заголовком Swift (формат Swift-модифицированный Markdown)
|
||
|
||
/**
|
||
Операция приветствия
|
||
|
||
- Маркер в документировании
|
||
- Еще один маркер в документации
|
||
|
||
- Parameter name : Это имя
|
||
- Parameter day : Это день
|
||
- Returns : Строка, содержащая значения name и day.
|
||
*/
|
||
func greet(name: String, day: String) -> String {
|
||
return "Привет \(name), сегодня \(day)."
|
||
}
|
||
greet(name: "Боб", day: "вторник")
|
||
|
||
// как и выше, кроме обращения параметров функции
|
||
func greet2(name: String, externalParamName localParamName: String) -> String {
|
||
return "Привет \(name), сегодня \(localParamName)"
|
||
}
|
||
greet2(name: "Иван", externalParamName: "Воскресенье")
|
||
|
||
// Функция, которая возвращает множество элементов в кортеже
|
||
func getGasPrices() -> (Double, Double, Double) {
|
||
return (3.59, 3.69, 3.79)
|
||
}
|
||
let pricesTuple = getGasPrices()
|
||
let price = pricesTuple.2 // 3.79
|
||
// Пропускайте значения кортежей с помощью подчеркивания _
|
||
let (_, price1, _) = pricesTuple // price1 == 3.69
|
||
print(price1 == pricesTuple.1) // вывод: true
|
||
print("Цена газа: \(price)")
|
||
|
||
// Именованные параметры кортежа
|
||
func getGasPrices2() -> (lowestPrice: Double, highestPrice: Double, midPrice: Double) {
|
||
return (1.77, 37.70, 7.37)
|
||
}
|
||
let pricesTuple2 = getGasPrices2()
|
||
let price2 = pricesTuple2.lowestPrice
|
||
let (_, price3, _) = pricesTuple2
|
||
print(pricesTuple2.highestPrice == pricesTuple2.1) // вывод: true
|
||
print("Самая высокая цена за газ: \(pricesTuple2.highestPrice)")
|
||
|
||
// guard утверждения
|
||
func testGuard() {
|
||
// guards обеспечивают прерывание дальнейшего выполнения функции,
|
||
// позволяя держать обработчики ошибок рядом с проверкой условия
|
||
// Объявляемая переменная находится в той же области видимости, что и guard.
|
||
guard let aNumber = Optional<Int>(7) else {
|
||
return
|
||
}
|
||
|
||
print("число равно \(aNumber)")
|
||
}
|
||
testGuard()
|
||
|
||
// Переменное число аргументов
|
||
func setup(numbers: Int...) {
|
||
// это массив
|
||
let number = numbers[0]
|
||
let argCount = numbers.count
|
||
}
|
||
|
||
// Передача и возврат функций
|
||
func makeIncrementer() -> ((Int) -> Int) {
|
||
func addOne(number: Int) -> Int {
|
||
return 1 + number
|
||
}
|
||
return addOne
|
||
}
|
||
var increment = makeIncrementer()
|
||
increment(7)
|
||
|
||
// передача по ссылке
|
||
func swapTwoInts(a: inout Int, b: inout Int) {
|
||
let tempA = a
|
||
a = b
|
||
b = tempA
|
||
}
|
||
var someIntA = 7
|
||
var someIntB = 3
|
||
swapTwoInts(a: &someIntA, b: &someIntB)
|
||
print(someIntB) // 7
|
||
|
||
|
||
//
|
||
// MARK: Замыкания
|
||
//
|
||
var numbers = [1, 2, 6]
|
||
|
||
// Функции - это частный случай замыканий ({})
|
||
|
||
// Пример замыкания.
|
||
// `->` отделяет аргументы и возвращаемый тип
|
||
// `in` отделяет заголовок замыкания от тела замыкания
|
||
numbers.map({
|
||
(number: Int) -> Int in
|
||
let result = 3 * number
|
||
return result
|
||
})
|
||
|
||
// Когда тип известен, как и выше, мы можем сделать так
|
||
numbers = numbers.map({ number in 3 * number })
|
||
// Или даже так
|
||
//numbers = numbers.map({ $0 * 3 })
|
||
|
||
print(numbers) // [3, 6, 18]
|
||
|
||
// Хвостовое замыкание
|
||
numbers = numbers.sorted { $0 > $1 }
|
||
|
||
print(numbers) // [18, 6, 3]
|
||
|
||
// Суперсокращение, поскольку оператор < выполняет логический вывод типов
|
||
|
||
numbers = numbers.sorted(by: <)
|
||
|
||
print(numbers) // [3, 6, 18]
|
||
|
||
//
|
||
// MARK: Структуры
|
||
//
|
||
|
||
// Структуры и классы имеют очень похожие характеристики
|
||
struct NamesTable {
|
||
let names: [String]
|
||
|
||
// Пользовательский индекс
|
||
subscript(index: Int) -> String {
|
||
return names[index]
|
||
}
|
||
}
|
||
|
||
// У структур автогенерируемый (неявно) инициализатор
|
||
let namesTable = NamesTable(names: ["Иван", "Яков"])
|
||
let name = namesTable[1]
|
||
print("Имя :\(name)") // Имя: Яков
|
||
|
||
//
|
||
// MARK: Обработка ошибок
|
||
//
|
||
|
||
// Протокол `Error` используется для перехвата выбрасываемых ошибок
|
||
enum MyError: Error {
|
||
case badValue(msg: String)
|
||
case reallyBadValue(msg: String)
|
||
}
|
||
|
||
// фунции помеченные словом `throws` должны вызываться с помощью `try`
|
||
func fakeFetch(value: Int) throws -> String {
|
||
guard 7 == value else {
|
||
throw MyError.reallyBadValue(msg: "Действительно плохое значение")
|
||
}
|
||
|
||
return "тест"
|
||
}
|
||
|
||
func testTryStuff() {
|
||
// предполагается, что не будет выброшено никаких ошибок,
|
||
// в противном случае мы получим рантайм исключение
|
||
let _ = try! fakeFetch(value: 7)
|
||
|
||
// Если возникает ошибка, то выполнение продолжится. Но если значение равно nil,
|
||
// то результат будет опционалом
|
||
let _ = try? fakeFetch(value: 7)
|
||
|
||
do {
|
||
// обычно try оператор, позволяющий обработать ошибку в `catch` блоке
|
||
try fakeFetch(value: 1)
|
||
} catch MyError.badValue(let msg) {
|
||
print("Ошибка: \(msg)")
|
||
} catch {
|
||
// все остальное
|
||
}
|
||
}
|
||
testTryStuff()
|
||
|
||
//
|
||
// MARK: Классы
|
||
//
|
||
|
||
// Классы, структуры и их члены имеют трехуровневый контроль доступа
|
||
// Уровни: internal (по умолчанию), public, private
|
||
|
||
public class Shape {
|
||
public func getArea() -> Int {
|
||
return 0
|
||
}
|
||
}
|
||
|
||
// Все методы и свойства класса являются открытыми (public).
|
||
// Если вам необходимо содержать только данные
|
||
// в структурированном объекте, вы должны использовать `struct`
|
||
|
||
internal class Rect: Shape {
|
||
var sideLength: Int = 1
|
||
|
||
// Пользовательский сеттер и геттер
|
||
private var perimeter: Int {
|
||
get {
|
||
return 4 * sideLength
|
||
}
|
||
set {
|
||
// `newValue` - неявная переменная, доступная в сеттере
|
||
sideLength = newValue / 4
|
||
}
|
||
}
|
||
|
||
// Вычисляемые свойства должны быть объявлены с помощью `var`, ведь они могут меняться
|
||
var smallestSideLength: Int {
|
||
return self.sideLength - 1
|
||
}
|
||
|
||
// Ленивая загрузка свойства
|
||
// свойство subShape остается равным nil (неинициализированным),
|
||
// пока не вызовется геттер
|
||
lazy var subShape = Rect(sideLength: 4)
|
||
|
||
// Если вам не нужны пользовательские геттеры и сеттеры,
|
||
// но все же хотите запустить код перед и после вызовов геттера или сеттера
|
||
// свойств, вы можете использовать `willSet` и `didSet`
|
||
var identifier: String = "defaultID" {
|
||
// аргумент у `willSet` будет именем переменной для нового значения
|
||
willSet(someIdentifier) {
|
||
print(someIdentifier)
|
||
}
|
||
}
|
||
|
||
init(sideLength: Int) {
|
||
self.sideLength = sideLength
|
||
// последним всегда вызывается super.init, когда init с параметрами
|
||
super.init()
|
||
}
|
||
|
||
func shrink() {
|
||
if sideLength > 0 {
|
||
sideLength -= 1
|
||
}
|
||
}
|
||
|
||
override func getArea() -> Int {
|
||
return sideLength * sideLength
|
||
}
|
||
}
|
||
|
||
// Простой класс `Square` наследует `Rect`
|
||
class Square: Rect {
|
||
convenience init() {
|
||
self.init(sideLength: 5)
|
||
}
|
||
}
|
||
|
||
var mySquare = Square()
|
||
print(mySquare.getArea()) // 25
|
||
mySquare.shrink()
|
||
print(mySquare.sideLength) // 4
|
||
|
||
// преобразование объектов
|
||
let aShape = mySquare as Shape
|
||
|
||
// сравнение экземпляров, в отличие от ==, которая проверяет эквивалентность
|
||
if mySquare === mySquare {
|
||
print("Ага, это mySquare")
|
||
}
|
||
|
||
// Опциональная инициализация (init)
|
||
class Circle: Shape {
|
||
var radius: Int
|
||
override func getArea() -> Int {
|
||
return 3 * radius * radius
|
||
}
|
||
|
||
// Поместите постфиксный знак вопроса после `init` - это и будет опциональная инициализация,
|
||
// которая может вернуть nil
|
||
init?(radius: Int) {
|
||
self.radius = radius
|
||
super.init()
|
||
|
||
if radius <= 0 {
|
||
return nil
|
||
}
|
||
}
|
||
}
|
||
|
||
var myCircle = Circle(radius: 1)
|
||
print(myCircle?.getArea()) // Optional(3)
|
||
print(myCircle!.getArea()) // 3
|
||
var myEmptyCircle = Circle(radius: -1)
|
||
print(myEmptyCircle?.getArea()) // "nil"
|
||
if let circle = myEmptyCircle {
|
||
// не будет выполняться, поскольку myEmptyCircle равен nil
|
||
print("circle не nil")
|
||
}
|
||
|
||
|
||
//
|
||
// MARK: Перечисления
|
||
//
|
||
|
||
// Перечисления могут быть определенного или своего типа.
|
||
// Они могут содержать методы подобно классам.
|
||
|
||
enum Suit {
|
||
case spades, hearts, diamonds, clubs
|
||
func getIcon() -> String {
|
||
switch self {
|
||
case .spades: return "♤"
|
||
case .hearts: return "♡"
|
||
case .diamonds: return "♢"
|
||
case .clubs: return "♧"
|
||
}
|
||
}
|
||
}
|
||
|
||
// Значения перечислений допускают сокращенный синтаксис, нет необходимости
|
||
// указывать тип перечисления, когда переменная объявляется явно
|
||
var suitValue: Suit = .hearts
|
||
|
||
// Значения нецелочисленных перечислений должны быть указаны явно
|
||
// или могут выводится с помощью функции `rawValue` из имени
|
||
enum BookName: String {
|
||
case john
|
||
case luke = "Лука"
|
||
}
|
||
print("Имя: \(BookName.john.rawValue)")
|
||
|
||
// Перечисление (enum) со связанными значениями
|
||
enum Furniture {
|
||
// Связать с типом Int
|
||
case desk(height: Int)
|
||
// Связать с типами String и Int
|
||
case chair(String, Int)
|
||
|
||
func description() -> String {
|
||
switch self {
|
||
case .desk(let height):
|
||
return "Письменный стол высотой \(height) см."
|
||
case .chair(let brand, let height):
|
||
return "Стул марки \(brand) высотой \(height) см."
|
||
}
|
||
}
|
||
}
|
||
|
||
var desk: Furniture = .desk(height: 80)
|
||
print(desk.description()) // "Письменный стол высотой 80 см."
|
||
var chair = Furniture.chair("Foo", 40)
|
||
print(chair.description()) // "Стул марки Foo высотой 40 см."
|
||
|
||
|
||
//
|
||
// MARK: Протоколы
|
||
//
|
||
|
||
// `protocol` может потребовать, чтобы у соответствующих типов
|
||
// были определенные свойства экземпляра, методы экземпляра, тип методов,
|
||
// операторы и индексы.
|
||
|
||
protocol ShapeGenerator {
|
||
var enabled: Bool { get set }
|
||
func buildShape() -> Shape
|
||
}
|
||
|
||
// Протоколы, объявленные с @objc, допускают необязательные функции,
|
||
// которые позволяют вам проверять на соответствие. Для функций также необходимо указать @objc
|
||
@objc protocol TransformShape {
|
||
@objc optional func reshape()
|
||
@objc optional func canReshape() -> Bool
|
||
}
|
||
|
||
class MyShape: Rect {
|
||
var delegate: TransformShape?
|
||
|
||
func grow() {
|
||
sideLength += 2
|
||
|
||
// Размещайте знак вопроса перед опционным свойством, методом
|
||
// или индексом, чтобы не учитывать nil-значение и возвратить nil
|
||
// вместо выбрасывания ошибки выполнения (т.н. "опционная цепочка")
|
||
if let reshape = self.delegate?.canReshape?(), reshape {
|
||
// проверка делегата на выполнение метода
|
||
self.delegate?.reshape?()
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// MARK: Прочее
|
||
//
|
||
|
||
// `extension`s: Добавляет расширенную функциональность к существующему типу
|
||
|
||
// Класс Square теперь "соответствует" протоколу `CustomStringConvertible`
|
||
extension Square: CustomStringConvertible {
|
||
var description: String {
|
||
return "Площадь: \(self.getArea()) - ID: \(self.identifier)"
|
||
}
|
||
}
|
||
|
||
print("Объект Square: \(mySquare)")
|
||
|
||
// Вы также можете расширить встроенные типы
|
||
extension Int {
|
||
var customProperty: String {
|
||
return "Это \(self)"
|
||
}
|
||
|
||
func multiplyBy(num: Int) -> Int {
|
||
return num * self
|
||
}
|
||
}
|
||
|
||
print(7.customProperty) // "Это 7"
|
||
print(14.multiplyBy(num: 3)) // 42
|
||
|
||
// Обобщения: Подобно языкам Java и C#. Используйте ключевое слово `where`,
|
||
// чтобы определить условия обобщений.
|
||
|
||
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
|
||
for (index, value) in array.enumerated() {
|
||
if value == valueToFind {
|
||
return index
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
let foundAtIndex = findIndex(array: [1, 2, 3, 4], valueToFind: 3)
|
||
print(foundAtIndex == 2) // вывод: true
|
||
|
||
// Операторы:
|
||
// Пользовательские операторы могут начинаться с символов:
|
||
// / = - + * % < > ! & | ^ . ~
|
||
// или
|
||
// Unicode- знаков математики, символов, стрелок, декорации и линий/кубов,
|
||
// нарисованных символов.
|
||
prefix operator !!!
|
||
|
||
// Префиксный оператор, который утраивает длину стороны, когда используется
|
||
prefix func !!! (shape: inout Square) -> Square {
|
||
shape.sideLength *= 3
|
||
return shape
|
||
}
|
||
|
||
// текущее значение
|
||
print(mySquare.sideLength) // 4
|
||
|
||
// Используя пользовательский оператор !!!, изменится длина стороны
|
||
// путем увеличения размера в 3 раза
|
||
!!!mySquare
|
||
print(mySquare.sideLength) // 12
|
||
|
||
// Операторы также могут быть обобщенными
|
||
infix operator <->
|
||
func <-><T: Equatable> (a: inout T, b: inout T) {
|
||
let c = a
|
||
a = b
|
||
b = c
|
||
}
|
||
|
||
var foo: Float = 10
|
||
var bar: Float = 20
|
||
|
||
foo <-> bar
|
||
print("foo это \(foo), bar это \(bar)") // "foo = 20.0, bar = 10.0"
|
||
```
|