Swift_rus Flashcards
Code examples and Q&A
`struct Tutorial {
var difficulty: Int = 1
}
var tutorial1 = Tutorial() var tutorial2 = tutorial1 tutorial2.difficulty = 2`
Чему равны значения tutorial1.difficulty и tutorial2.difficulty? Была бы какая-то разница, если бы Tutorial был классом? Почему?
tutorial1.difficulty равен 1, а tutorial2.difficulty равен 2.
В Swift структуры — типы-значения (value type). Они копируются, а не ссылаются. Следующая строка копирует tutorial1 и присваивает её tutorial2:
var tutorial2 = tutorial1
Изменения в tutorial2 не отражаются на tutorial1.
Если бы Tutorial был бы классом, tutorial1.difficulty и tutorial2.difficulty равнялись бы 2. Классы в Swift — ссылочные типы (reference type). Когда вы меняете свойство tutorial1, вы увидите такое же изменение у tutorial2 — и наоборот.
Вы объявили view1 при помощи var, а view2 — при помощи let. В чём разница и скомпилируется ли последняя строка?
import UIKit
var view1 = UIView() view1.alpha = 0.5
let view2 = UIView() view2.alpha = 0.5 // Эта строка скомпилируется?
Да, последняя строка скомпилируется. view1 — это переменная, и вы можете назначить её значение новым экземпляром UIView. Используя let, вы можете присвоить значение лишь однажды, так что следующий код не скомпилируется:
view2 = view1 // Ошибка: view2 is immutable
Однако, UIView — это класс со ссылочной семантикой, так что вы можете изменять свойства view2 — что означает, что код скомпилируется.
Этот код сортирует массив по алфавиту. Максимально упростите замыкание.
var animals = ["fish", "cat", "chicken", "dog"] animals.sort { (one: String, two: String) -> Bool in return one < two } print(animals)
Swift автоматически определяет тип параметров замыкания и возвращаемый тип, так что вы можете убрать их:
animals.sort { (one, two) in return one < two }
Вы можете заменить имена параметров использованием нотации $i:
animals.sort { return $0 < $1 }
Замыкания, состоящие из одного оператора, могут не содержать ключевое слово return. Значение последнего выполненного оператора становится возвращаемым результатом замыкания:
animals.sort { $0 < $1 }
Наконец, так как Swift знает, что элементы массива соответствуют протоколу Equatable, вы можете просто написать:
animals.sort(by:
Этот код создаёт два класса: Address и Person. Также создаются два экземпляра класса Person (Ray и Brian).
class Address { var fullAddress: String var city: String
init(fullAddress: String, city: String) { self.fullAddress = fullAddress self.city = city } }
class Person { var name: String var address: Address
init(name: String, address: Address) { self.name = name self.address = address } }
var headquarters = Address(fullAddress: "123 Tutorial Street", city: "Appletown") var ray = Person(name: "Ray", address: headquarters) var brian = Person(name: "Brian", address: headquarters)
Предположим, что Brian переехал по новому адресу и вы хотите обновить его запись следующим образом:
brian.address.fullAddress = “148 Tutorial Street”
Это компилируется и выполняется без ошибок. Но, если вы проверите теперь адрес Ray, то вы увидите, что он тоже «переехал».
Что здесь произошло и как мы можем исправить это?
Address — это класс и обладает ссылочной семантикой. Таким образом, headquarters — это один и тот же экземпляр класса, который разделяют ray и brian. Изменение headquarters изменит адрес обоих.
Чтобы исправить это, можно создать новый экземпляр класса Address и присвоить его Brian, или объявить Address как struct вместо class.
Что такое optional и какие проблемы они решают?
optional позволяет переменной любого типа представить ситуацию “отсутствие значения”. В Objective-C «отсутствие значения» было доступно только в ссылочных типах с использованием специального значения nil. У типов-значений (value types), вроде int или float, такой возможности не было.
Swift расширил концепцию «отсутствия значения» на типы-значения. Переменная optional может содержать либо значение, либо nil, сигнализирующее об отсутствии значения.
Коротко перечислите основные отличия между structure и class.
Классы поддерживают наследование, а структуры — нет.
Классы — ссылочный тип, структуры — тип-значение.
Что такое generics и для чего они нужны?
В Swift вы можете использовать generics в классах, структурах и перечислениях.
Generics устраняют проблему дублирования кода. Если у вас есть метод, который принимает параметры одного типа, иногда приходится дублировать код, чтобы работать с параметрами другого типа.
Например, в этом коде вторая функция — это «клон» первой, за исключением того, что у неё параметры string, а не integer.
func areIntEqual(_ x: Int, _ y: Int) -> Bool { return x == y }
func areStringsEqual(_ x: String, _ y: String) -> Bool { return x == y }
areStringsEqual(“ray”, “ray”) // true
areIntEqual(1, 1) // true
Применяя generics, вы совмещаете две функции в одной и одновременно обеспечиваете безопасность типов:
func areTheyEqual(_ x: T, _ y: T) -> Bool { return x == y }
areTheyEqual(“ray”, “ray”)
areTheyEqual(1, 1)
Так как вы тестируете равенство, вы ограничиваете типы теми, которые отвечают протоколу Equatable. Этот код обеспечивает требуемый результат и препятствует передаче параметров неподходящего типа.
В некоторых случаях не получится избежать неявного разворачивания (implicitly unwrapped) optionals. Когда и почему?
Наиболее частые причины для использования implicitly unwrapped optionals:
когда вы не можете инициализировать свойство, которое не nil в момент создания. Типичный пример — outlet у Interface Builder, который всегда инициализируется после его владельца. В этом особенном случае, если в Interface Builder всё правильно сконфигурировано — вам гарантировано, что outlet не-nil перед его использованием.
чтобы разрешить проблему цикла сильных ссылок, когда два экземпляра классов ссылаются друг на друга и требуется не-nil ссылка на другой экземпляр. В этом случае вы помечаете ссылку на одной стороне как unowned, а на другой стороне используете неявное разворачивание optional.
Какими способами можно развернуть optional? Оцените их в смысле безопасности.
var x : String? = “Test”
Принудительное развёртывание (forced unwrapping) — небезопасно.
let a: String = x!
Неявное развертывание при объявлении переменной — небезопасно.
var a = x!
Optional binding — безопасно.
if let a = x { print("x was successfully unwrapped and is = \(a)") }
Optional chaining — безопасно.
let a = x?.count
Nil coalescing operator — безопасно.
let a = x ?? “”
Оператор Guard — безопасно.
guard let a = x else { return }
Optional pattern — безопасно.
if case let a? = x {
print(a)
}
Замыкания — это ссылочный тип или тип-значение?
Замыкания — это ссылочный тип. Если вы присваиваете переменной замыкание, а затем копируете в другую переменную, вы копируете ссылку на то же самое замыкание и его список захвата.
Опишите циклические ссылки в Swift? Как их можно исправить?
Циклические ссылки происходят, когда два экземпляра содержат сильную ссылку друг на друга, что приводит к утечке памяти из-за того, что ни один из этих экземпляров не может быть освобождён. Экземпляр не может быть освобождён, пока есть еще сильные ссылки на него, но один экземпляр держит другой.
Это можно разрешить, заменив на одной из сторон ссылку, указав ключевое слово weak или unowned.