Irbisa · Шпаргалка

Продвинутый Dart

Обобщения, расширения, примеси, изоляты, продвинутый dart:core

Middle-разработчик · 15 вопросов

  1. 01

    Что такое изолят в Dart и когда стоит выносить в него работу?

    Изолят представляет собой независимый Dart-воркер со своей памятью и своим event loop, поэтому он выполняется по-настоящему параллельно с UI-изолятом.

    import 'dart:convert';import 'dart:isolate'; class User {  final String name;  User(this.name);…
  2. 02

    Что на самом деле делает модификатор late и когда он неуместен?

    late позволяет присвоить non-nullable переменную после объявления.

    class _EditorState extends State<Editor>    with SingleTickerProviderStateMixin {  // lazy: the initializer needs `this`, so it runs on first read  late final AnimationController _anim = AnimationController(    vsync: this,    duration: const Duration(milliseconds: 200),…
  3. 03

    В крашлоге Bad state: No element, и стектрейс указывает на вызов firstWhere. Что произошло и что писать вместо этого?

    Ничего не совпало.

    import 'package:collection/collection.dart'; class Order {  final bool isPending;  final int cents;  const Order(this.isPending, this.cents);…
  4. 04

    Как работают дженерики и ограничения типов (bounds) в Dart и зачем они переиспользуемым виджетам и data-классам?

    Дженерик-класс или функция принимает параметр типа, который подставляет вызывающий код: Page<T> превращается в Page<User>.

    // A bound lets the body call compareTo on TT maxOf<T extends Comparable<T>>(List<T> xs) =>    xs.reduce((a, b) => a.compareTo(b) >= 0 ? a : b); // A generic function: T is fixed per callT first<T>(List<T> xs) => xs.first;…
  5. 05

    Найдите баг: markSeen падает во время выполнения вместо того, чтобы запомнить, что фото просмотрено.

    class Photo { Photo(this.id); final int id; }class Meta { bool seen = false; } // Per-photo UI flags without touching Photo.final _meta = Expando<Meta>('photoMeta'); void markSeen(Photo photo) {  final meta = _meta[photo.id] ?? Meta();  meta.seen = true;  _meta[photo.id] = meta;} void main() {  markSeen(Photo(7));  print('marked');}

    Expando привязывает данные к объекту, и ключ должен быть настоящим объектом.

  6. 06

    Extension-методы: что выведет эта программа и какое одно правило объясняет все четыре строки?

    class Animal {}class Dog extends Animal {}extension AnimalX on Animal { String speak() => 'animal'; }extension DogX on Dog { String speak() => 'woof'; } class Cat { String speak() => 'meow'; }extension CatX on Cat { String speak() => 'purr'; } void main() {  final Animal a = Dog();  print(a.speak());  print(Dog().speak());  print(Cat().speak());  final dynamic d = Dog();  print(d.speak());}

    Она выведет animal, woof, meow, а затем бросит NoSuchMethodError.

  7. 07

    Почему list.map(...) ничего не выполняет до вызова toList() и когда эта ленивость бьёт по рукам?

    map, where, expand, take и skip возвращают ленивый Iterable, а не список: ваш колбэк выполняется только тогда, когда результат действительно обходят.

    void main() {  final nums = [1, 2, 3];   final doubled = nums.map((n) {    print('mapping $n'); // does not run yet    return n * 2;…
  8. 08

    Что такое генераторы sync* и async* и когда их стоит писать?

    Генератор выдаёт последовательность по одному значению через yield.

    // sync*: values are produced only as the caller asks for themIterable<int> naturals() sync* {  var n = 0;  while (true) {    yield n++; // naturals().take(3) gives 0, 1, 2  }…
  9. 09

    Что выведет эта программа с генератором sync*?

    Iterable<int> count() sync* {  print('start');  for (var i = 1; i <= 3; i++) {    print('yield $i');    yield i;  }  print('end');} void main() {  final numbers = count();  print('created');  print(numbers.first);  print(numbers.first);}

    Она выведет created, затем start, yield 1, 1, а потом ещё раз start, yield 1, 1.

  10. 10

    Что такое Uint8List и другие typed-data списки и когда использовать их вместо List<int>?

    Uint8List, Int32List, Float64List и остальные классы dart:typed_data представляют собой списки фиксированной длины, хранящиеся одним плотным блоком «сырых» чисел.

    import 'dart:typed_data'; void main() {  final bytes = Uint8List(4);  bytes[0] = 300;  print(bytes[0]); // 44: only the low 8 bits are kept…
  11. 11

    Найдите баг: byId[selected.id] печатает null, хотя selected взят из того же самого списка строк.

    class Row {  Row(this.id, this.text);  final int id;  final String text;} var _nextId = 0;Row _toRow(String line) => Row(_nextId++, line); void main() {  final rows = ['a', 'b', 'c'].map(_toRow);  final selected = rows.first;  final byId = {for (final r in rows) r.id: r};  print(byId[selected.id]?.text);}

    map ленив: rows не список, а рецепт, который вызывает _toRow заново при каждом проходе.

  12. 12

    Что происходит между Dart-исходниками и release-бинарником (kernel, gen_snapshot, tree shaking)?

    Фронтенд компилятора переводит Dart в kernel (.dill); в release gen_snapshot превращает всю программу в машинный код (libapp.so на Android, App.framework на iOS) после того, как tree shaking по всей программе выбросил всё недостижимое.

  13. 13

    Как работает сборщик мусора в Dart (молодое и старое поколение) и что это значит для Flutter-кода?

    В Dart поколенческий сборщик мусора.

  14. 14

    Есть ли в Dart рефлексия? Почему dart:mirrors недоступен во Flutter и чем его заменяют?

    Рефлексия в Dart есть (dart:mirrors), но только в standalone VM.

  15. 15

    Как WeakReference и Expando позволяют GC забрать закешированные объекты и почему Finalizer не деструктор?

    Обычный Map хранит сильные ссылки, поэтому всё в нём живёт столько же, сколько сам кеш.

    class DecodedImage { DecodedImage(this.bytes); final int bytes; } // a cache the garbage collector is allowed to emptyclass ThumbCache {  final _entries = <String, WeakReference<DecodedImage>>{};…