Особенности const в Dart и Flutter

Умные книжки, умные люди и умный линтер советуют использовать const-константы. Но, почему так важно использовать это магическое слово, и когда его важно не использовать?
В этой статье раскроем особенности работы const-констант и ответим на эти вопросы.

Что есть const?

В Dart есть два вида констант — final и const. Наше внимание остановим на последней.

const — это модификатор переменных для создания compile-time констант. После создания объекта с данным модификатором, он целиком и полностью становится неизменяемым.

Особенности const-констант:

  • const переменные должны полностью создаваться в compile-time.

  • const переменные являются глубоко-транзитивно-неизменяемыми. Т.е. при использовании данного модификатора неизменяемыми становятся все внутренние глубинные объекты данного объекта. Например если двумерный список сделать const, то подсписки также замораживаются и становятся неизменяемыми.

  • const объекты канонизируются (canonicalize) — об этом стоит поговорить подробнее.

Сanonicalization — канонизация const-констант

Все const константы канонизируются и затем используются для определения эквивалентности значений одной const константы другим const константам.

Сanonicalization — канонизация — это процесс преобразования значения const-переменной в стандартное каноническое представление. Это сделано для того, чтобы значения нескольких const-переменных можно было сравнивать друг с другом и находить одинаковые.

Если у двух объектов, на которые ссылаются const-переменные, канонические представления совпадают — это означает, что данные объекты ничем друг от друга не отличаются.
Поэтому в памяти можно держать только один объект, а множество переменных будет ссылаться на него. Также канонические представления облегчают операции сравнения, что оптимизирует работу различных алгоритмов.

Для построения канонизировнного представления значения const-переменной, виртуальная машина Dart VM учитывает значения и тип аргументов, которые использовались для создания данного объекта. Примеры представлены ниже на рисунках 1 и 2.

Рис.1 - Пример канонизации констант с встроенным типом данных

Рис. 1 — Пример канонизации констант с встроенным типом данных

Рис.2 - Пример канонизации констант с кастомным типом данных

Рис. 2 — Пример канонизации констант с кастомным типом данных

const-константы не пересоздаются каждый раз.
Для значений const констант вычисляется каноническое представление и сохраняется в специальной таблице поиска (хэш канонического представления => значение объекта).
Впоследствии, если создается новая переменная-константа, вычисляется значение ее канонического представления и сравнивается с теми, что есть в таблице. Если в таблице есть объект для данного канонического представления, то новый объект не создается, а создается ссылка на старый. В противном случае, в таблицу добавляется новая запись с новым каноническим представлением, и переменная ссылается на него.

Канонизация const и Flutter

Константный конструктор позволяет создавать const-константы для экземпляров кастомных классов, которые канонизируются, и значение которых вместо дублирования может многократно использоваться.

Именно из-за этой особенности const-констант виджеты в флаттере советуют определять с константным конструктором. Единожды созданный виджет с константным конструктором будет реиспользоваться множество раз благодаря тому, что он был закэширован с его канонизированным представлением.

Однако, нужно использовать const-конструкторы с осторожностью. Если экземпляр класса может становится const-константой, это значит, что он может становится частью других констант.
Если константный класс находится глубоко в дереве виджетов внутри других константных виджетов, то добавление не final свойства или добавление final поля, которое не может быть проинциализировано в const конструкторе, приведет к тому, что придется рефакторить огромное количество кода.
Это может усложнить поддерживаемость кода.

Если в дереве виджетов какой-нибудь EdgeInsets используется 1000 раз, то кастомные виджеты могут использоваться гораздо реже. Из-за этого нет заметной пользы для производительности от их константности. Однако, есть большой вред для поддерживаемости пользовательских виджетов.

Константными стоит делать небольшие виджеты, вероятность модификаций которых крайне мала. Разумеется встроенные виджеты по типу EdgeInset можно также делать константными без вреда для поддерживаемости.

После завершения разработки и тестирования можно пройтись по дереву и добавить const конструкторы в неочевидные места, однако, скорее всего прирост к производительности не будет стоить потраченного времени.

Выводы

Const-константы в умелых руках могут стать отличным инструментом повышения производительности приложений на Dart/Flutter. Однако, если использовать их не грамотно, то они могут навредить.

Преждевременная оптимизация — причина плохой поддерживаемости.

Связь со мной:  https://t.me/i_m_good_man

© Habrahabr.ru