Dart 点将台 | const 关键字知多少

2022-05-09 13:39:40 浏览数 (1)

1.对 const 的认知

80% 的人对 const 的认知,只停留在 const 修饰的量不可以被修改,这一条。 甚至有些人对什么时候可以用 const 修饰还比较模糊。首先看一下内置的类型变量对象的构造,如 intdoubleString,这些类型的对象可以使用 const 修饰。一旦被 const 修饰的量,就无法再做更改。

2.何时何处可以用 const 修饰

下面先看一下,什么时候能用 const,什么时候不能用 const。如下 Person 类中有一个 name 成员。 对于一个 Person 对象而已,是不能使用 const 修饰的。错误原因,红线处也有提示:

代码语言:javascript复制
[1]. 无 const 构造方法创建的对象,无法使用 const 修饰。 
复制代码

既然提示说,想要 const 修饰对象,其对应构造需要为 const,那就在构造前加个 const 呗。这时你会发现上面的声明不飘红了,但下面的 const 修饰的构造方法飘红了。提示说 :

代码语言:javascript复制
[2].使用 const 构造方法的类中,其所有成员属性必须以 final 修饰。
复制代码

所以这时将 namefinal 修饰,就可以不报错。从上面两点可以得出一个推论:

代码语言:javascript复制
[推论]: 使用 const 修饰的对象,其各属性也是无法修改的。
复制代码

总结一下 const 可修饰的位置:在声明变量时,可以将 const 放在前面,也可以将 const 放在等号后,修饰构造函数;const 可作为修饰符,修饰构造函数。 只有在修饰 的时候,该 才不允许被修改。比如下面 toly2 可以赋为其他值,但 toly 不可以。

3.const 在类中的注意点
代码语言:javascript复制
[3]: const 构造方法不能有方法体。
复制代码
代码语言:javascript复制
[4]: 类中的 const 修饰的成员,必须为 static 静态的。
复制代码

你在源码中可以看到,使用 const 修饰的成员,都会由 static 修饰。

4.const 的价值

如下:a ,b 对象声明为 const,c 对象使用普通构造,可见 a 和 b 是全等的,由于没有在 Person 类中重写 == 运算符,则说明这两个量在 运行时 同一块内存地址。这样的好处在于:即使在代码中用了 10000 次 const Person(name: 'toly') ,都是同一对象,都是同一块内存空间。这便是 const 常量的优势,该对象在代码编译期间 就已经确定的。

而对于非 const 创建的对象,用了 10000 次 Person(name: 'toly') ,就是创建了 10000 个对象,每个对象都占着一份空间。 所以这样看来,对于一些不变的常量,使用 const 修饰是很有价值的。

代码语言:javascript复制
main() {
  Person a = const Person(name: 'toly');
  Person b = const Person(name: 'toly');
  Person c = Person(name: 'toly');
  print(a == b); // true
  print(a == c); // false
}
复制代码
5. const 常量在 Flutter 中的使用

这时,你再反观 Flutter 中的一些东西,就会有更多的感悟,比如 Text 组件的构造器使用了 const 修饰,就说明 Text 对象可以使用 const 进行修饰,并且 Text 类中的所有属性都必须为 final。

这样,对应不变的文字信息,可以使用 const 进行修饰,这样,下次 build 时这个量就可以直接使用,而非构造新的 Text 对象。Flutter 中的很多组件和属性都有 const 构造,如果它们是不变的,最好使用 const 修饰。

代码语言:javascript复制
const Text('张风捷特烈')
复制代码

但要注意:const 是编译期常量,你不能在其中使用运行时的计算,这样编译是无法通过的。

6.关于const 的层级

如下, Padding 及其属性都是常量,作用两边,是否等价?是否需要一一加 const

这里来做一个小实验,在 Position 类中添加 Position 属性。 ab 对象构造如下,a 只用一个 const 修饰,b 每个对象都用 const 修饰。结果显示,a 和 b 是相等的 , a.positionb.position 也是相等的。这就说明,左右写法是一致的,所以没必要每个对象上都加 const 修饰。

代码语言:javascript复制
main() {
  Person a = const Person(
    name: "张风捷特烈",
    position: Position(
      position: 4
    )
  );

  Person b = const Person(
      name: "张风捷特烈",
      position: const Position(
          position: 4
      )
  );

  print(a == b); // true
  print(a.position == b.position); // true
}

class Person {
  final String name;
  final Position position;
  const Person({this.name = '张风捷特烈',this.position});
}

class Position{
  final int position;
  const Position({this.position = 0});
}
复制代码

越上层使用 const 要求是更严格的,其下都必须都是常量才可以。如下,TextStyle 中 color 使用了 withOpacity 方法,是运行时的,所以构造出的 TextStyle 就不是常量,最上层的 Padding 自然也就不能使用 const 修饰。

同理这里 Text 也无法使用 const 修饰,所以,只能将 const 给 EdgeInsets.all(8.0) 修饰。也就是说,能用 const 修饰的尽量用 const 修饰。

关于 const 在用法上的一些细节点,就讲到这里,FlutterUnit 之前没太注意这方面,现在已经优化完毕。现在你也该想一想,你的常量 const 了吗 ~ 下面小结一下:

代码语言:javascript复制
[1]. 无 const 构造方法创建的对象,无法使用 const 修饰。 
[2]. 使用 const 构造方法的类中,其所有成员属性必须以 final 修饰。
[推论]: 使用 const 修饰的对象,其各属性也是无法修改的。
[3]. const 构造方法不能有方法体。
[4]: 类中的 const 修饰的成员,必须为 static 静态的。
[5]. const 构造方法中传入的值必须是 const 对象。
复制代码
7. debug 模式下的 Dart 常量去重策略

昨天有人在群里问了 const 为什么在运行时不相等:

因为 debug 模式 下的 Dart 常量去重策略导致的。在官网有相关介绍文章 《调试 Flutter 应用》。其实很容易理解,在 debug 时,需要追踪 Widget 的创建时机,而 const 对象在编译期间就已经初始化了。这是 debug 模式 下的 常量去重策略的必要性。当然你可以通过如下方式来关闭这个 去重策略 ,但一般来说这并没有什么必要。

代码语言:javascript复制
flutter run --no-track-widget-creation
复制代码

如下,在 release 模式下运行,就不会出现这种现象。App 的开发还是以 release 模式为准,所以不必太过纠结。

0 人点赞