Перечисления
Enumerations
• Полноценный тип (наравне с классами)
• Обладает свойствами класса
конструкторы, вычисляемые свойства,
динамические методы (у объектов)
• Поддерживает расширения (extensions -
категории) и протоколы (protocols)
enum Animal {
// значения
case Dog
case Cat
case Fish
case Robot
}
enum LazyAnimal {
// Одного 'case' достаточно
case Dog, Cat, Fish, Robot
}
По умолчанию значениям не присваиваются
Int эквиваленты
Каждое значение имеет тип своего перечисления
let pet = Animal.Dog
switch pet {
case .Dog:
println("WOF-WOF")
case .Cat:
println("MEOW")
case .Fish:
println("???")
case .Robot:
println("Death to humans!")
}
“Ассоциированные” значения
• Каждый объект перечисления может
содержать объект другого типа в качестве
соответствия
• В таком случае их типы могут быть разными
• Позволяет “ассоциировать” любую
информацию
enum Barcode {
case UPCA(Int, Int, Int)
case PDF417(String)
}
var productBarcode = Barcode.UPCA(1, 2, 3)
productBarcode = .PDF417("ABCD")
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
// Syntax sugar
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
// Syntax sugar
switch productBarcode {
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
// Syntax sugar
switch productBarcode {
case let .UPCA(system, ident, check):
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
// Syntax sugar
switch productBarcode {
case let .UPCA(system, ident, check):
println("UPCA with value of (system), (ident), (check).")
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
// Syntax sugar
switch productBarcode {
case let .UPCA(system, ident, check):
println("UPCA with value of (system), (ident), (check).")
case let .PDF417(code):
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
// Syntax sugar
switch productBarcode {
case let .UPCA(system, ident, check):
println("UPCA with value of (system), (ident), (check).")
case let .PDF417(code):
println("PDF417 with value of (code).")
Ассоциированные значения доступны
в switch-case конструкции
switch productBarcode {
case .UPCA(let system, let ident, let check):
println("UPCA with value of (system), (ident), (check).")
case .PDF417(let code):
println("PDF417 with value of (code).")
}
// Syntax sugar
switch productBarcode {
case let .UPCA(system, ident, check):
println("UPCA with value of (system), (ident), (check).")
case let .PDF417(code):
println("PDF417 with value of (code).")
}
“Замещающие” значения
• Каждый объект перечисления может быть
сопоставлен с объектом другого типа
• “Замещающие” значения не меняются (в
отличие от “ассоциированных”
• Допустимые типы:
Character, String, Double, Float, Int*
// для Int работает auto-increment
enum Number: Int {
case One = 1, Two, Three, Four, Five, Six, Seven, Eight,
FortyTwo = 42
}
enum Letter: Character {
case A = "a"
case B = "b"
case C = "c"
}
let answerToTheAllQuestions = Number.FortyTwo
answerToTheAllQuestions.toRaw()
let twoOnTwo = Number.fromRaw(2 * 2)
twoOnTwo?.toRaw()
*
// свой тип для перечислений
final class CustomRawType: IntegerLiteralConvertible, Equatable {
let number: Int
let square: Int
let cube: Int
class func convertFromIntegerLiteral(value: Int) -> CustomRawType {
return CustomRawType(number: value)
}
init(number: Int) {
self.number = number
self.square = number * number
self.cube = number * number * number
}
func == (lhs: CustomRawType, rhs: CustomRawType) -> Bool {
return lhs.number == rhs.number
}
}
• Структура или final класс
• Equatable протокол
• Любой *LiteralConvertible протокол
enum Number2: CustomRawType {
case One = 1
case Two = 2
case Three = 3
}
let three = Number2.Three
let rawThree = three.toRaw()
rawThree.cube
Generiс
• Одна из самых интересных особенностей Swift
• Повышает гибкость и переиспользуемость кода
• Swift Standard Library во многом написана с
использованием Generic типов
Generic функции
func genericSwap<T>(inout a: T, inout b: T) {
let temp = b
b = a
a = temp
}
var a_s = "a"
var b_s = "a"
var a_i = 1
var b_i = 2
genericSwap(&a_s, &b_s)
genericSwap(&a_i, &b_i)
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
class Example<T: MyClass, U: MyProtocol> { }
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
class Example<T: MyClass, U: MyProtocol> { }
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
class Example<T: MyClass, U: MyProtocol> { }
// compile error
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
class Example<T: MyClass, U: MyProtocol> { }
// compile error
// let example = Example<Int, Double>()
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
class Example<T: MyClass, U: MyProtocol> { }
// compile error
// let example = Example<Int, Double>()
let example2 = Example<MyClass, MyClassWithProtocol>()
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
class Example<T: MyClass, U: MyProtocol> { }
// compile error
// let example = Example<Int, Double>()
let example2 = Example<MyClass, MyClassWithProtocol>()
let example3 = Example<MyClass2, MyClassWithProtocol>()
Ограничения на типы
class MyClass { }
protocol MyProtocol { }
class MyClassWithProtocol: MyProtocol { }
class MyClass2: MyClass { }
class MyClass3: MyClass, MyProtocol { }
class Example<T: MyClass, U: MyProtocol> { }
// compile error
// let example = Example<Int, Double>()
let example2 = Example<MyClass, MyClassWithProtocol>()
let example3 = Example<MyClass2, MyClassWithProtocol>()
let example4 = Example<MyClass3, MyClass3>() // OK
Ассоциированные типы
protocol Stack {
typealias ObjectType
func push(object: ObjectType)
func pop()
func peek() -> ObjectType?
}
ObjectType будет объявлен позже:
в классе, который реализует протокол
// Stack protocol автоматически возьмёт T
// в качестве типа для typealias ObjectType
class AnotherGenericStack<T>: Stack {
var elements = [T]()
func isEmpty() -> Bool {
return elements.isEmpty
}
func push(object: T) {
elements.append(object)
}
func pop() {
elements.removeLast()
}
func peek() -> T? {
return elements.last
}
}
class IntStack: Stack {
var elements = [Int]()
func isEmpty() -> Bool {
return elements.isEmpty
}
// Для не-generic типов нужно
// явно объявить тип для typealias ObjectType
typealias ObjectType = Int
func push(object: ObjectType) {
elements.append(object)
}
func pop() {
elements.removeLast()
}
func peek() -> ObjectType? {
return elements.last
}
}
Зачем нужны протоколы
1. Безопасность
Протоколы гарантируют проверку на реализацию
всех необходимых методов и свойств
Зачем нужны протоколы
1. Безопасность
Протоколы гарантируют проверку на реализацию
всех необходимых методов и свойств
2. Упрощенное использование
Протоколы позволяют разработчику понять, какими
свойствами обладает класс, структура или
перечисление, не зная конкретный тип
protocol Vehicle {
var wheels: Int { get set }
var isRequiresFuel: Bool { get }
func start() -> String
}
class Car: Vehicle {
var wheels: Int = 4
let isRequiresFuel: Bool = true
func start() -> String {
return "Сar starts its journey"
}
}
var myCar = Car()
myCar.wheels = 6
myCar.wheels // 6
myCar.isRequiresFuel // true
myCar.start() // "Сar starts its journey"
class Bike: Vehicle {
var wheels: Int = 2
var isRequiresFuel: Bool
init(isWithFuel: Bool) {
self.isRequiresFuel = isWithFuel
}
func start() -> String {
return "Bike starts its journey"
}
}
var myTransport: Vehicle
myTransport = Bike(isWithFuel: false)
myTransport.wheels // 2
myTransport.isRequiresFuel // false
myTransport.start() // "Bike starts its journey"
myTransport = Car()
myTransport.wheels // 4
enum OnOffSwitch: Togglable {
case Off, On
mutating func toggle() {
switch self {
case Off:
self = On
case On:
self = Off
}
}
var state: String {
get {
switch self {
case Off:
return "Off"
case On:
return "On"
}
}
}
}
Природа Optionals
Optional тип - это на самом деле enum,
который может содержать либо .None либо .Some(T)
Optionals позволяют отследить момент отсутствия
значения у переменной
Опциональность в Obj-C
У NSObject (супер-класс) могло быть значение nil
(NULL).
Для Int и прочих базовых типов использовались
константы (к примеру NSNotFound), которые
определяли какое-то значение (к примеру -1 или
INT_MAX)
Обозначения
T? - опциональный тип
optional type
T! - косвенно раскрывающийся опциональный тип
implicitly unwrapped optional
v? - вызов цепочки от опционального типа
optional chaining
v! - распаковка опционального типа
unwrapping optional
var optionalObject: AnyObject? = nil
var object: AnyObject = nil
COMPILATION ERROR: Type 'AnyObject' does
not conform to the protocol
'NilLiteralConvertible
Использование и
базовые операции
var optionalString: String? // nil
optionalString = "Content"
// Unwrapping. error if optionalString == nil
optionalString!
optionalString? // Optional chaining
optionalString?.hasPrefix("Con")
optionalString.hasPrefix("") COMPILE ERROR:
String? does not have a member named “hasPrefix”
if let string = optionalString { // Safe unwrap
// Use string
}
Первый “?” позволяет вызвать метод если object != nil
Второй “?” вызывает method(params) только если у объекта
таковой реализован (respondsToSelector в obj-c)
object?.method?(params)
var jsonContent: AnyObject?
jsonContent?.objectForKey?
("content")?.objectForKey?("info")?[5]
Косвенно раскрывающийся
опциональный тип
implicitly unwrapped optionals
var unwrappedOptionalString: String! // nil
unwrappedOptionalString = "Content" // "Content"
if let str = unwrappedOptionalString {
str.hasPrefix("co") // false
// Work with str
}
unwrappedOptionalString.hasPrefix("Co") // true
unwrappedOptionalString = nil
unwrappedOptionalString?.hasPrefix("Co") // nil
unwrappedOptionalString.hasPrefix("Co") RUNTIME ERROR:
Execution was interrupted, reason: EXC_BAD_INSTRUCTION
(code=EXC_I386_INVOP, subcode=0x0)