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

#flutter #dart

Вопрос:

Учитывая эти два простых шаблона проектирования для статических значений, каковы последствия каждого из них? Идентичны ли они по производительности и использованию памяти в приложениях Dart/Flutter при доступе к их участникам?

Пример А: MyStringsGetters

 const strings = MyStringsGetters();

class MyStringsGetters {
  const MyStringsGetters();
  get helloWorld => 'Hello, World';
}
 

Пример B: MyStringsMembers

 const strings = MyStringsMembers();

class MyStringsMembers {
  const MyStringsMembers();
  static const helloWorld = 'Hello, World';
}
 

Ответ №1:

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

Я бы предложил пойти с примером B.

Вот мое доказательство. Я создал фиктивное приложение, которое отображает 100 экземпляров текста, отображающих строку Hello World, используя оба примера, которыми вы поделились, и вот результаты:

Пример А Пример B
введите описание изображения здесь введите описание изображения здесь

Вы можете ясно видеть, что выделенная память намного меньше, Example B и основная причина в том, что получатель выделит новые адреса памяти.

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

 import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

const strings = MyStringsMembers();

class MyStringsMembers {
  const MyStringsMembers();
  static const helloWorld = 'Hello, World';
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: MyStringsMembers.helloWorld,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Widget> _widgets() {
    List<Widget> result = [];
    for (var i = 0; i < 10000; i  ) {
      result.add(Text(MyStringsMembers.helloWorld));
    }
    return resu<
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
            mainAxisAlignment: MainAxisAlignment.center, children: _widgets()),
      ),
    );
  }
}

 

Я попытался просмотреть документацию и не смог найти много об этом. Самая близкая информация, которую я нашел в книге спецификаций языка программирования Dart, страница 37.

Геттеры-это функции (9), которые используются для извлечения значений свойств объекта.

Учитывая, что вы не сохраняете значение строки как свойство объекта, а геттер, по сути, является просто вспомогательной функцией, которая выполняет то, что вы указываете, в конечном итоге при каждом вызове будет выделяться память. "Hello world" по сути, это означает создание новой строки в памяти с этим содержимым.

Комментарии:

1. Это была моя интуитивная мысль, и я согласен с вашими утверждениями/подходом. Я не смог найти никаких документов, подтверждающих утверждение: «Геттеры-это, по сути, методы, которые вызываются во время выполнения, и их ответ не кэшируется». У вас есть какая-либо письменная документация, подтверждающая это? Мне нравится читать об этом; в противном случае, отличный ответ!

2. Я приложил ответ к вашему комментарию в ответе. Короче говоря, я не смог найти много информации о геттерах, но я нашел некоторые доказательства, подтверждающие, что геттеры-это просто функции, которые можно использовать для извлечения значения различных объектов. Учитывая, что объект, который вы извлекаете с помощью вашего геттера, не является свойством объекта, и вы динамически выделяете строку, не сохраняя ее нигде, вы в конечном итоге создаете новые экземпляры каждый раз, когда вызывается функция.

3. Спасибо! Я очень ценю это.

Ответ №2:

Вроде. Пример A, геттер будет вызван для объекта MyStringsGetters. т. е. strings.helloWorld

Но пример B, HelloWorld будет вызываться на MyStringsMembers. т. Е. вы не можете вызывать strings.helloWorld , как в примере A.

Вы должны использовать версию примера B, если одно и то же значение перенастроено из геттера в классе независимо от экземпляра (отсюда static ключевое слово).

Но пример А может быть изменен на

 void main(){
  print(MyClass("Hello World").value);
  print(MyClass("Thank you").value);
}

class MyClass {
  final String _value;
  const MyClass(String value) : _value=value;
  String get value => _value;
}
 

Вы получите результат

 Hello World
Thank you
 

MyClass по-прежнему является a const , что означает, что все возможные значения MyClass будут известны как время компиляции.