Download - Swift School #2
Swift Школа
Сергей Пронин Empatika
План
• Перечисления
• Generic
• Протоколы
• Optionals
Перечисления
Перечисления 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-case конструкции
switch productBarcode {
Ассоциированные значения доступны в switch-case конструкции
switch productBarcode {case .UPCA(let system, let ident, let check):
Ассоциированные значения доступны в 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 sugarswitch 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 sugarswitch 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 sugarswitch 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 sugarswitch 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 sugarswitch 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 sugarswitch 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
Generic
Generiс
• Одна из самых интересных особенностей Swift
• Повышает гибкость и переиспользуемость кода
• Swift Standard Library во многом написана с использованием Generic типов
Generic функции
Generic функции
func genericSwap<T>(inout a: T, inout b: T) {
Generic функции
func genericSwap<T>(inout a: T, inout b: T) { let temp = b
Generic функции
func genericSwap<T>(inout a: T, inout b: T) { let temp = b b = a
Generic функции
func genericSwap<T>(inout a: T, inout b: T) { let temp = b b = a a = temp
Generic функции
func genericSwap<T>(inout a: T, inout b: T) { let temp = b b = a a = temp}
Generic функции
func genericSwap<T>(inout a: T, inout b: T) { let temp = b b = a a = temp}
Generic функции
func genericSwap<T>(inout a: T, inout b: T) { let temp = b b = a a = temp}
var a_s = "a"
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"
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
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 = 1var b_i = 2
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 = 1var b_i = 2genericSwap(&a_s, &b_s)
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 = 1var b_i = 2genericSwap(&a_s, &b_s)genericSwap(&a_i, &b_i)
Generic типы
Generic типыclass GenericStack<T> { var elements = [T]() func push(object: T) { elements.append(object) } func pop() { elements.removeLast() } func peek() -> T? { return elements.last } func isEmpty() -> Bool { return elements.isEmpty } }
Generic типыclass GenericStack<T> { var elements = [T]() func push(object: T) { elements.append(object) } func pop() { elements.removeLast() } func peek() -> T? { return elements.last } func isEmpty() -> Bool { return elements.isEmpty } }
let intStack = GenericStack<Int>() intStack.push(12) intStack.push(123) intStack.pop()
let stringStack = GenericStack<String>() stringStack.push("a") stringStack.push("ba") stringStack.pop()
Ограничения на типы
Ограничения на типыclass MyClass { }
Ограничения на типыclass MyClass { }protocol MyProtocol { }
Ограничения на типыclass MyClass { }protocol MyProtocol { }class MyClassWithProtocol: MyProtocol { }
Ограничения на типы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
protocol Togglable { mutating func toggle() var state: String { get } }
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" } } } }
var switcher: Togglable = OnOffSwitch.On switcher.state // "On" switcher.toggle() switcher.state // “Off"
func getLamp() -> Togglable
var currentLamp: Togglable = getLamp() currentLamp.toggle()
Optionals
Природа 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
enum Optional<T> : Reflectable, NilLiteralConvertible { case None case Some(T) init() init(_ some: T)
/// Haskell's fmap, which was mis-named func map<U>(f: (T) -> U) -> U? func getMirror() -> MirrorType static func convertFromNilLiteral() -> T? }
var optionalValue = Optional<Int>(5) // {Some 5} optionalValue = nil // nil optionalValue = 10 // {Some 10} optionalValue = Optional<Int>.None // nil optionalValue = Optional.Some(15) // {Some 15} optionalValue = Optional.convertFromNilLiteral() // nil
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 optionalsvar 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)
Вопросы