Сметанин Иван
iOS разработчик в Почте
Learning from
Swift sources
30.10.2019
План
2
• Пройдемся по стандартной библиотеке
• Научимся реализовывать словари
• Познакомимся с forums.swift.org
Swift Standard Library
3
• Swift - Open Source (вдруг кто не знал)
• Stdlib живет в swift/stdlib/public/core
Dictionary.swift
4
Hash function
5
let name = “John Smith"
name.hashValue // Вот вам и хэш фукнция
(2800094741206366432)
Функция, преобразующая входное значение в
Integer
Коллизии
6
Коллизии
7
Open hashing
Closed hashing
(Open addressing)
(Separate chaining)
firstnamefirstname firstname
lastname
lastname
birthdate
birthdate
5
4
0
3 3
1
2
3
Open hashing
8
firstnamefirstname firstname
lastname
lastname
birthdate birthdate
5
4
0
3
1
2
3
Closed hashing
9
Элемент который мы
хотим вставить
Его позиция
по хэшу
x 3
5
4
0
6
7
3
1
2
b
a
c
d
r
s
t
3
0
0
2
2
2
4
4
Linear probing
10
Элемент, который мы
хотим вставить
Его позиция
по хэшу
x 3
5
4
0
6
7
3
1
2
b
a
c
d
r
s
t
3
0
0
2
2
2
4
4
5
4
0
6
7 x
3
1
2
b
a
c
d
r
s
t
3
0
0
2
2
2
4
4
3
Linear probing
11
Элемент, который мы
хотим вставить
Его позиция
по хэшу 5
4
0
6
7
3
1
2
b
a
c
d
r
s
t
3
0
0
2
2
2
4
4
x 3
Robin-hood hashing
12
5
4
0
6
7
3
1
2
b
a
c
d
r
x
t
3
0
0
2
2
2
3
4
s 4
Элемент, который мы
забрали из словаря в
обмен на x
Robin-hood hashing
13
Бенчмарки
14
Вставляем в Hash-table 80% от максимума
элементов (lfm 0.8)
Удаляем 10% от максимума и тут же
вставляем обратно
DIB - расстояние до начальной точки
Делаем так 50 раз и замеряем статистику на
каждой итерации
Данные: http://codecapsule.com/2013/11/11/robin-hood-hashing/
Суммируем
15
• В Swift closed hashing + linear probing
• Robin Hood hashing это прикольно, но
пользоваться можно только в Rust
• Теперь можно пройти собеседование в
Google (нет)
ArraySlice.swift
16
ArraySlice
17
var array = [0, 1, 2, 3, 4, 5]
var slice = array[1..<4]
0 1 2 3 4 5 6
Slice
“Ломтик” массива
let greeting = "Hi there! It's nice to meet you! 👋"
let endOfSentence = greeting.firstIndex(of: "!")!
let firstSentence = greeting[...endOfSentence]
label.text = firstSentence
Substring
18
let greeting = "Hi there! It's nice to meet you! 👋"
let endOfSentence = greeting.firstIndex(of: "!")!
let firstSentence = greeting[...endOfSentence]
label.text = String(firstSentence)
// String(_:Substring) копирует буфер
Substring
19
Start
Count = 13
Owner
“Hello, world!”
Hello, world!
Start
Count = 5
Owner
“world”
Capacity, reference count
Owning class
Substring
20
Hello, world!
Start
Count = 5
Owner
“world”
Capacity, reference count
Owning class
Substring
21
ContiguousArray.swift
22
let array = [1, 2, 3]
let contiguousArray = ContiguousArray([1, 2, 3])
ContiguousArray
23
Массив который хранит элементы в цельном регионе памяти
Non-Contiguous
Contiguous
24
class ShellsSortStrategy: SortStrategy {
required init(array: [User]) {
self.array = array
}
var array: [User]
func sort() {
// Тут скучный код сортировки
}
}
25
func testShellsSortBaseStrategy() {
let strategy = ShellsSortStrategy(array: array)
self.measure {
strategy.sort()
}
}
let array = [User](repeating: User(name: ""), count: 1000000)
Измеряем время исполнения
Среднее время исполнения
Замер (всего 10 за сессию)
Время текущего замера с его
отклонением от среднего
26
class ShellsSortStrategy: SortStrategy {
required init(array: [User]) {
self.array = ContiguousArray(array)
}
// Теперь у нас не просто Array
var array: ContiguousArray<User>
func sort() {
// Тут скучный код сортировки
}
}
Зачем ContiguousArray?
27
CollectionDifference.swift
28
let oldArray = ["a", "b", "c", "d"]
let newArray = ["a", "b", "d"]
let diff = newArray.difference(from: oldArray)
CollectionDifference
29
Дифф алгоритм прямо в стандартной библиотеке 😱
Просмотр изменений
for change in diff {
switch change {
case let .remove(offset, _, _):
anotherArray.remove(at: offset)
case let .insert(offset, newElement, _):
anotherArray.insert(newElement, at: offset)
}
}
CollectionDifference
30
CollectionDifference
31
var anotherArray = oldArray
// Применяем изменения #
anotherArray = anotherArray.applying(diff)!
// Теперь `anotherArray` равен `newArray`
Применение изменений
32
X A B C D
X
Y
C
D •
Старт
Текущая коллекция
Новая коллекция
Финиш
•
Правила переходов по матрице:
• Слева-направо

(удаление)
• Сверху вниз

(добавление)
• Или диагональ

(элементы совпадают)
33
X A B C D
X
Y
C
D
•
•
(0, 0)
Старт
Тут нечего делать
1 2 3 4 50
0
1
2
3
4
34
X A B C D
X
Y
C
D
•
•
(0, 0)
(1, 1)
Старт
•
Тут нечего делать
Диагональный ход, так как X и X
1 2 3 4 50
0
1
2
3
4
35
X A B C D
X
Y
C
D
•
•
(0, 0)
(1, 1)
(2, 1)
Старт
• •
Тут нечего делать
Диагональный ход, так как X и X
Идем вправо, удаление А
1 2 3 4 50
0
1
2
3
4
36
X A B C D
X
Y
C
D
•
•
(0, 0)
(1, 1)
(2, 1)
(3, 1)
Старт
• • •
Тут нечего делать
Диагональный ход, так как X и X
Идем вправо, удаление А
Идем вправо, удаление В
1 2 3 4 50
0
1
2
3
4
37
X A B C D
X
Y
C
D
•
•
(0, 0)
(1, 1)
(2, 1)
(3, 1)
(3, 2)
Старт
• • •
•
Тут нечего делать
Диагональный ход, так как X и X
Идем вправо, удаление А
Идем вправо, удаление В
Идем вниз, добавление Y
1 2 3 4 50
0
1
2
3
4
38
X A B C D
X
Y
C
D
•
•
(0, 0)
(1, 1)
(2, 1)
(3, 1)
(3, 2)
(4, 3)
Старт
• • •
•
•
Тут нечего делать
Диагональный ход, так как X и X
Идем вправо, удаление А
Идем вправо, удаление В
Идем вниз, добавление Y
Диагональ, С и С совпадают
1 2 3 4 50
0
1
2
3
4
39
X A B C D
X
Y
C
D
•
•
(0, 0)
(1, 1)
(2, 1)
(3, 1)
(3, 2)
(4, 3)
(5, 4)
Старт
Финиш
• • •
•
•
Тут нечего делать
Диагональный ход, так как X и X
Идем вправо, удаление А
Идем вправо, удаление В
Идем вниз, добавление Y
Диагональ, С и С совпадают
Диагональ, D и D совпадают
1 2 3 4 50
0
1
2
3
4
Как там с перфомансом?
40
Время
0,00
0,01
0,02
0,03
0,04
0,05
0,06
DeepDiff Differ ListDiff CollectionDifference
0,004
0,02
0,057
0,015
От 2000 элементов к 2100: 100 вставок и 200 удалений
Collection DiffableDataSource
41
Collection DiffableDataSource
42
Можно не смешивать CollectionDifference и UITableView
самому, Apple сделала это за нас
UITableViewDiffableDataSource
43
Было
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: “id",
for: indexPath
)
// и т.д
return cell
}
tableView.dataSource = self
tableView.reloadData()
UITableViewDiffableDataSource
44
// И никаких больше cellForRow

UITableViewDiffableDataSource<Section, User>(
tableView: tableView,
cellProvider: { tableView, indexPath, contact in
let cell = tableView.dequeueReusableCell(
withIdentifier: reuseIdentifier,
for: indexPath
)
cell.textLabel?.text = contact.name
return cell
}
)
Создание
UITableViewDiffableDataSource
45
var snapshot = NSDiffableDataSourceSnapshot<Section, User>()
snapshot.appendSections(Section.allCases)
snapshot.appendItems(userList, toSection: .users)
dataSource.apply(
snapshot,
animatingDifferences: animate,
completion: nil
)
Изменение
46
TV Shows
Search
Next to watch
Popular on Netflix
100
%
9:41
Суммируем
47
• ArraySlice/Substring нужны чтобы не копировать каждый
раз строки и их лучше не хранить слишком долго
• ContiguousArray может помочь сэкономить время
исполнения
• CollectionDifference можно взять вместо очередной
библиотеки, но iOS 13+
• UITableViewDiffableDataSource очень круто, но тоже iOS
13+
forums.swift.org
48
forums.swift.org
49
• Это как programmersforum.ru на
максималках и про Swift
• Можно поучаствовать в развитии языка
• Или спросить очередной SO вопрос
forums.swift.org
50Ссылочка
forums.swift.org
51
Cсылочка: https://forums.swift.org/t/class-and-final-class-between-swift-4-2-and-swift-5-performance/21804
52
class ShellsSortStrategy: SortStrategy {
required init(array: [Int]) {
self.array = ContiguousArray(array)
}
var array: ContiguousArray<Int>
func sort() {
// Тут скучный код сортировки
}
}
53
final class ShellsSortStrategy: SortStrategy {
required init(array: [Int]) {
self.array = ContiguousArray(array)
}
var array: ContiguousArray<Int>
func sort() {
// Тут скучный код сортировки
}
}
ContiguousArray
54
Базовый пример ContiguousArray Final + ContiguousArray
Иван
Сметанин
iOS разработчик в почте mail.ru
@ismetanin

Learning from Swift sources, Иван Сметанин

  • 1.
    Сметанин Иван iOS разработчикв Почте Learning from Swift sources 30.10.2019
  • 2.
    План 2 • Пройдемся постандартной библиотеке • Научимся реализовывать словари • Познакомимся с forums.swift.org
  • 3.
    Swift Standard Library 3 •Swift - Open Source (вдруг кто не знал) • Stdlib живет в swift/stdlib/public/core
  • 4.
  • 5.
    Hash function 5 let name= “John Smith" name.hashValue // Вот вам и хэш фукнция (2800094741206366432) Функция, преобразующая входное значение в Integer
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
    Элемент который мы хотимвставить Его позиция по хэшу x 3 5 4 0 6 7 3 1 2 b a c d r s t 3 0 0 2 2 2 4 4 Linear probing 10
  • 11.
    Элемент, который мы хотимвставить Его позиция по хэшу x 3 5 4 0 6 7 3 1 2 b a c d r s t 3 0 0 2 2 2 4 4 5 4 0 6 7 x 3 1 2 b a c d r s t 3 0 0 2 2 2 4 4 3 Linear probing 11
  • 12.
    Элемент, который мы хотимвставить Его позиция по хэшу 5 4 0 6 7 3 1 2 b a c d r s t 3 0 0 2 2 2 4 4 x 3 Robin-hood hashing 12
  • 13.
    5 4 0 6 7 3 1 2 b a c d r x t 3 0 0 2 2 2 3 4 s 4 Элемент, которыймы забрали из словаря в обмен на x Robin-hood hashing 13
  • 14.
    Бенчмарки 14 Вставляем в Hash-table80% от максимума элементов (lfm 0.8) Удаляем 10% от максимума и тут же вставляем обратно DIB - расстояние до начальной точки Делаем так 50 раз и замеряем статистику на каждой итерации Данные: http://codecapsule.com/2013/11/11/robin-hood-hashing/
  • 15.
    Суммируем 15 • В Swiftclosed hashing + linear probing • Robin Hood hashing это прикольно, но пользоваться можно только в Rust • Теперь можно пройти собеседование в Google (нет)
  • 16.
  • 17.
    ArraySlice 17 var array =[0, 1, 2, 3, 4, 5] var slice = array[1..<4] 0 1 2 3 4 5 6 Slice “Ломтик” массива
  • 18.
    let greeting ="Hi there! It's nice to meet you! 👋" let endOfSentence = greeting.firstIndex(of: "!")! let firstSentence = greeting[...endOfSentence] label.text = firstSentence Substring 18
  • 19.
    let greeting ="Hi there! It's nice to meet you! 👋" let endOfSentence = greeting.firstIndex(of: "!")! let firstSentence = greeting[...endOfSentence] label.text = String(firstSentence) // String(_:Substring) копирует буфер Substring 19
  • 20.
    Start Count = 13 Owner “Hello,world!” Hello, world! Start Count = 5 Owner “world” Capacity, reference count Owning class Substring 20
  • 21.
    Hello, world! Start Count =5 Owner “world” Capacity, reference count Owning class Substring 21
  • 22.
  • 23.
    let array =[1, 2, 3] let contiguousArray = ContiguousArray([1, 2, 3]) ContiguousArray 23 Массив который хранит элементы в цельном регионе памяти Non-Contiguous Contiguous
  • 24.
    24 class ShellsSortStrategy: SortStrategy{ required init(array: [User]) { self.array = array } var array: [User] func sort() { // Тут скучный код сортировки } }
  • 25.
    25 func testShellsSortBaseStrategy() { letstrategy = ShellsSortStrategy(array: array) self.measure { strategy.sort() } } let array = [User](repeating: User(name: ""), count: 1000000) Измеряем время исполнения Среднее время исполнения Замер (всего 10 за сессию) Время текущего замера с его отклонением от среднего
  • 26.
    26 class ShellsSortStrategy: SortStrategy{ required init(array: [User]) { self.array = ContiguousArray(array) } // Теперь у нас не просто Array var array: ContiguousArray<User> func sort() { // Тут скучный код сортировки } }
  • 27.
  • 28.
  • 29.
    let oldArray =["a", "b", "c", "d"] let newArray = ["a", "b", "d"] let diff = newArray.difference(from: oldArray) CollectionDifference 29 Дифф алгоритм прямо в стандартной библиотеке 😱
  • 30.
    Просмотр изменений for changein diff { switch change { case let .remove(offset, _, _): anotherArray.remove(at: offset) case let .insert(offset, newElement, _): anotherArray.insert(newElement, at: offset) } } CollectionDifference 30
  • 31.
    CollectionDifference 31 var anotherArray =oldArray // Применяем изменения # anotherArray = anotherArray.applying(diff)! // Теперь `anotherArray` равен `newArray` Применение изменений
  • 32.
    32 X A BC D X Y C D • Старт Текущая коллекция Новая коллекция Финиш • Правила переходов по матрице: • Слева-направо
 (удаление) • Сверху вниз
 (добавление) • Или диагональ
 (элементы совпадают)
  • 33.
    33 X A BC D X Y C D • • (0, 0) Старт Тут нечего делать 1 2 3 4 50 0 1 2 3 4
  • 34.
    34 X A BC D X Y C D • • (0, 0) (1, 1) Старт • Тут нечего делать Диагональный ход, так как X и X 1 2 3 4 50 0 1 2 3 4
  • 35.
    35 X A BC D X Y C D • • (0, 0) (1, 1) (2, 1) Старт • • Тут нечего делать Диагональный ход, так как X и X Идем вправо, удаление А 1 2 3 4 50 0 1 2 3 4
  • 36.
    36 X A BC D X Y C D • • (0, 0) (1, 1) (2, 1) (3, 1) Старт • • • Тут нечего делать Диагональный ход, так как X и X Идем вправо, удаление А Идем вправо, удаление В 1 2 3 4 50 0 1 2 3 4
  • 37.
    37 X A BC D X Y C D • • (0, 0) (1, 1) (2, 1) (3, 1) (3, 2) Старт • • • • Тут нечего делать Диагональный ход, так как X и X Идем вправо, удаление А Идем вправо, удаление В Идем вниз, добавление Y 1 2 3 4 50 0 1 2 3 4
  • 38.
    38 X A BC D X Y C D • • (0, 0) (1, 1) (2, 1) (3, 1) (3, 2) (4, 3) Старт • • • • • Тут нечего делать Диагональный ход, так как X и X Идем вправо, удаление А Идем вправо, удаление В Идем вниз, добавление Y Диагональ, С и С совпадают 1 2 3 4 50 0 1 2 3 4
  • 39.
    39 X A BC D X Y C D • • (0, 0) (1, 1) (2, 1) (3, 1) (3, 2) (4, 3) (5, 4) Старт Финиш • • • • • Тут нечего делать Диагональный ход, так как X и X Идем вправо, удаление А Идем вправо, удаление В Идем вниз, добавление Y Диагональ, С и С совпадают Диагональ, D и D совпадают 1 2 3 4 50 0 1 2 3 4
  • 40.
    Как там сперфомансом? 40 Время 0,00 0,01 0,02 0,03 0,04 0,05 0,06 DeepDiff Differ ListDiff CollectionDifference 0,004 0,02 0,057 0,015 От 2000 элементов к 2100: 100 вставок и 200 удалений
  • 41.
  • 42.
    Collection DiffableDataSource 42 Можно несмешивать CollectionDifference и UITableView самому, Apple сделала это за нас
  • 43.
    UITableViewDiffableDataSource 43 Было func tableView(_ tableView:UITableView, numberOfRowsInSection section: Int) -> Int { return 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell( withIdentifier: “id", for: indexPath ) // и т.д return cell } tableView.dataSource = self tableView.reloadData()
  • 44.
    UITableViewDiffableDataSource 44 // И никакихбольше cellForRow
 UITableViewDiffableDataSource<Section, User>( tableView: tableView, cellProvider: { tableView, indexPath, contact in let cell = tableView.dequeueReusableCell( withIdentifier: reuseIdentifier, for: indexPath ) cell.textLabel?.text = contact.name return cell } ) Создание
  • 45.
    UITableViewDiffableDataSource 45 var snapshot =NSDiffableDataSourceSnapshot<Section, User>() snapshot.appendSections(Section.allCases) snapshot.appendItems(userList, toSection: .users) dataSource.apply( snapshot, animatingDifferences: animate, completion: nil ) Изменение
  • 46.
    46 TV Shows Search Next towatch Popular on Netflix 100 % 9:41
  • 47.
    Суммируем 47 • ArraySlice/Substring нужнычтобы не копировать каждый раз строки и их лучше не хранить слишком долго • ContiguousArray может помочь сэкономить время исполнения • CollectionDifference можно взять вместо очередной библиотеки, но iOS 13+ • UITableViewDiffableDataSource очень круто, но тоже iOS 13+
  • 48.
  • 49.
    forums.swift.org 49 • Это какprogrammersforum.ru на максималках и про Swift • Можно поучаствовать в развитии языка • Или спросить очередной SO вопрос
  • 50.
  • 51.
  • 52.
    52 class ShellsSortStrategy: SortStrategy{ required init(array: [Int]) { self.array = ContiguousArray(array) } var array: ContiguousArray<Int> func sort() { // Тут скучный код сортировки } }
  • 53.
    53 final class ShellsSortStrategy:SortStrategy { required init(array: [Int]) { self.array = ContiguousArray(array) } var array: ContiguousArray<Int> func sort() { // Тут скучный код сортировки } }
  • 54.
  • 55.