Крутим RunLoop. Как устроена лента ВКонтакте
Публикуем доклад с прошедшего митапа VK Tech Talks | iOS, где iOS-разработчик команды Ленты Александр Терентьев рассказывает о том, как работает лента «ВКонтакте».
Каждый видел новостную ленту «ВКонтакте». В ней находится много различных медиа-элементов это: фото, видео, GIF, аудио и большое количество заполнения ячеек. И это, в общем-то, основные вызовы. При этом очень много текста и очень много комбинаций.
Как описать и вычислить Layout?
AutoLayout
AutoLayout — это инструмент от Apple, API которого представляет собой систему ограничения (Layout constraints), то есть всё отображение описывается как система ограничения между view и точками определения этих view.
Под капотом лежит алгоритм Cassowary, который преобразует систему ограничений в задачу линейного программирования. В оригинале для решения используется симплекс-метод, который имеет экспоненциальную сложность. Но так как этот подход разрабатывается и улучшается Apple, Apple заявляет линейную сложность при вычислении очень простых отображений, то есть когда нет между view сложных ограничений, а всё в отдельных листочках собрано. И итоговые frames каждой view — это решение текущей оптимизационной задачи.
Преимущества AutoLayout:
Ограничения AutoLayout:
Когда использовать? Когда имеются отображения без scroll, без коллекций или коллекции с простыми элементами.
Ручной расчёт frame
Следующий механизм — это вручную прописать в коде весь Layout и посчитать размеры.
Преимущества:
Какие проблемы? Для сложных обращений может возникнуть очень много кода для описания Layout, в котором очень легко ошибиться.
Когда использовать?
Как вычислить размер текста?
Для того чтобы посчитать размер текста в Bubble, существует несколько подходов.
Методы UILabel/UITextView/UITextField
Первый подход — это использовать стандартные методы UILabel, UITextView и UITextField — sizeFields или IntrinsicContentSize, которые работают с AutoLayout.
Преимущества:
- Всё хорошо работает с AutoLayout.
- Размеры, возвращаемые этими методами, соответствуют размерам view, у которых эти методы вызваны. Но нельзя использовать на Background-очереди.
Кода использовать?
Методы NSAttributedString/NSString
Следующий подход использовать методы NSAttributedString и NSString. Такие методы как boundingRect и drawInRect (метод рисования строк). Их можно использовать для Background-очереди.
Но есть проблема то, что лучше использовать boundingRect и метод отрисовки строки. Это обуславливается тем, что когда используют метод boundingRect, добавляют «магические» числа. Это объясняется тем, что, например, в документации Apple не содержится обоснований того, что этот размер будет совпадать с размером стандартных элементов и непонятно, что под капотом у boundingRect.
Когда использовать?
- Когда имеются нестандартные отображения.
- Необходимо посчитать размер Background-очереди.
- Когда используется отрисовка на произвольном контексте. Например, необходимо отрисовать текст на картинке или что отрисовать в Background и использовать это.
TextKit
Следующий подход — это TextKit. Его преимуществами являются:
- Это очень мощный инструмент, который лежит в основе всех стандартных элементов UILabel, UITextView и UITextField.
- Этот инструмент это система классов, таких как NLayoutManager, NSTextStorage, NSTextContainer.
- Очень функциональный инструмент.
- Можно использовать в Background-очереди.
- Можно вычислять frame и количества строк в тексте.
- Можно указать фигуры, которые будут обтекаться текстом.
- Работает всё с iOS 7+.
Когда использовать?
CoreText
Следующий инструмент — это CoreText. Он лежит в основе TextKit. Его особенности:
Когда используется?
Как реализована Лента «ВКонтакте»
Процесс получения обработки данных Layout выглядит следующим образом: в Background-очереди получаются данные с сервера. После этого формируется описание, то есть декларативно описываем будущий Layout (здесь ещё нет отображения, есть только правила постановления отображения). Потом для этих правил определяется размер отображения. И составляется новый datasource или апдейт текущего datasource. Применяется полученный апдейт или новый datasource к таблице. Заполняются ячейки соответствующими элементами. И затем обрабатываются жесты от интерфейса.
Преимущества:
Причины лагов ленты:
Как убрать лаги?
RunLoop — это бесконечный цикл, на каждой операции которой обрабатываются различные события: таймеры, получение socket, обработка жестов от системы и т. д.
На каждой итерации RunLoop выполняются все блоки, которые добавлены в главную очередь, если происходит какой-то элемент от интерфейса, то есть пользователь сделал свайп, обрабатывается Pan. Также на итерации RunLoop, если имеются все сформированные Transaction, выполняется CATransaction.commit. Во время, когда scroll уже продолжается по инерции, начинает работать CADisplayLink — это такой таймер, который позволяет синхронизировать частоту обновлений экрана с количеством кадров, которые формирует приложение.




CATransaction — это блок апдейтов для иерархии слоёв, которые отображаются на экране. Внутри CATransaction.commit происходит Layout всех view, которые были помечены.
Процесс формирования кадра приложения выглядит так:


Какие операции являются дорогими и что с этим можно сделать:



Инструменты для измерений
Metal Instruments
Metal Instruments включает в себя Time Profiler, то есть здесь можно увидеть, что происходит, в каком потоке, в какой момент времени, но при этом здесь показывается, что происходит в этот момент на видеочипе. Также можно увидеть дорогие для Render Server и GPU операции, когда в потоке ничего не происходит, но при этом пропускает, потому что Render Server не успел отрисовать изображение для дисплея.


Особенности Metal Instruments:
- Нельзя использовать «на лету», то есть нужно сначала сформировать запись действий с интерфейсом, и только потом формируются метрики.
- Работает только на 64−битных девайсах.
Измеряем в коде
Для просмотра, как в данный момент работает приложение, на экране есть простой инструмент CADisplayLink, который имеет свойство как timestamp, в котором лежит время формирования прошлого кадра.


Есть проблема — не все лаги учитываются. Это связано с тем, что Render Server не всегда успевает обработать транзакции, что приведёт к пропуску кадра.
Как производить замеры:
Вот и всё!
Посмотреть видеозапись выступления можно здесь:
Лена Гамель для The Brown Room — независимого интернет-издания.
Корректор: Арсений Метелев
Фото: ВКонтакте
