1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Flutter-SU.md 9.9 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Эта статья посвящена анализу реализации некоторых синтаксических сахаров в Dart для Flutter. Она раскрывает, что же происходит за кулисами простых ключевых слов.

Начнем с того, что вчера в группе был задан очень базовый вопрос: "Почему эта часть кода не может проверять пустоту объекта user?"

На самом деле этот вопрос довольно прост:

  • В Dart при использовании звукового режима безопасности нулл-безопасности объявленные непустыми объекты не требуют проверки на пустоту; (хотя вы можете сделать такую проверку, но это вызовет предупреждение ⚠️);
  • Объекты, объявленные с помощью ключевого слова late, если они не были проинициализированы до обращения, приведут к ошибке.

Поэтому решение этого вопроса заключается просто в том, чтобы заменить User? user. Но почему объект, который не может быть пустым, становится допускающим отсроченную инициализацию после применения ключевого слова late?

late

Для начала рассмотрим следующий пример кода, где мы используем ключевое слово late для объявления объекта playerAnimation. После компиляции кода с помощью dump_kernel.dart мы можем извлечь информацию из файла app.dill.

Как видно из извлеченного кода, объект playerAnimation фактически преобразуется в nullable тип Animation?. При попытке доступа к нему через get playerAnimation(), если playerAnimation == null, будет выброшено исключение LateError.Таким образом, когда мы обращаемся к объекту, объявленному как late, и он ещё не был инициализирован, то получаем исключение.

typedef

Перейдём к ключевому слову typedef, которое с версии Dart 2.13 позволяет использовать новые возможности типа-синонима, такие как:

// Тип-синоним для функций (уже существовал)
typedef ValueChanged<T> = void Function(T value);

// Тип-синоним для классов (новая возможность)
typedef StringList = List<String>;

// Переименование классов без разрыва (новая возможность)
@Deprecated("Используйте NewClassName вместо")
typedef OldClassName<T> = NewClassName<T>;

Как работает typedef? Как показывают изображения, метод _getDeviceInfo после компиляции фактически заменяется на List<String>. Таким образом, StringList не участвует в выполнении скомпилированного кода и не влияет на производительность программы.

Допустим, как показано на следующем рисунке, можно заметить, что объявление selectItemChanged через SelectItemChanged после компиляции превращается в final поле (динамическое) → ? void selectItemChanged.

Затем мы используем концепцию tear-off в Dart для демонстрации другого явления. Как видно на следующем рисунке, мы извлекаем метод toString из произвольного объекта x, и с помощью замыкания можем вызывать его так же, как обычный метод объекта x.

> Если вы вызываете функцию на объекте и пропускаете скобки, Dart называет это "tear-off": замыкание, которое принимает те же аргументы, что и функция, и при вызове этого замыкания выполняется функция внутри него. Например, names.forEach(print); эквивалентно names.forEach((name) { print(name); });.

Как будет выглядеть метод getToString после компиляции?

На следующем рисунке можно увидеть, что метод getToString после компиляции становится статическим (static) методом, а также ToStringFn не участвует в выполнении и заменяется на соответствующий () -> core:String.

Поэтому для кода после компиляции использование ключевого слова typedef не оказывает влияния ни на производительность, ни на результат выполнения программы.

Расширение

В Dart с помощью extension можно легко расширять объекты. Как ключевое слово extension реализует расширение на основе существующего объекта?

На следующем рисунке показано объявление перечисления Cat и его расширение, чтобы каждому значению перечисления были присвоены значения, а также добавлен метод talk.

На следующем рисунке показано, что после компиляции значения перечисления Cat становятся статическими (static final) полями с постоянными адресами, а методы talk и value из CatExtension также переходят на новые места.

При рассмотрении соответствующей реализации было установлено, что методы name и talk из CatExtension стали статическими методами в том же файле, и метод talk был сначала определён как метод, а затем использован через tearoff для вызова. Большинство методов, определённых в расширении, имеют соответствующие методы и tearoffs.

Как показано на следующем рисунке, после компиляции вызов cat.talk() в месте использования Cat приводит к выполнению main::CatExtension|talk.

async / await

И последнее, поговорим о async / await. Мы знаем, что это синтаксический сахар для Future в Dart, но как этот синтаксический сахар выполняется после компиляции?

Можно заметить, что метод loadMore после компиляции получает множество дополнительных строк кода, где определяется _Future<void> async_future, которая возвращается в конце. При этом код, который нам нужно выполнить, оборачивается в async_op, а внутри этого блока происходит попытка выполнения содержимого с использованием try catch, и через _completeOnAsyncError возвращаются ошибки.

Это также объясняет, почему внешний try-catch для Future не может захватывать ошибки. Поэтому, как показано на следующем рисунке, для Future требуется использовать .onError((error, stackTrace) => null) для захвата и обработки ошибок.

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

Конечно, если вы не будете использовать эти знания, они могут пригодиться для "показухи" на собеседовании, не так ли?

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master