新的类型别名语言功能,改进的Dart FFI
作者:Kevin Moore和Michael Thomsen
今天,我们发布了Dart 2.13,其特点是类型别名--目前我们要求的第二大语言功能。Dart 2.13还包括改进的DFI和更好的性能,我们还为Dart提供了新的Docker官方图像。这篇文章给出了2.12版本中引入的空值安全功能的更新,讨论了2.13版本的新功能,有一些关于Docker和谷歌云对Dart后端支持的令人兴奋的消息,并预览了一些你可以在未来版本中看到的变化。
空值安全更新
我们在3月份的Dart 2.12版本中推出了完善的空值安全。空值安全是Dart最新的主要生产力特性,旨在帮助你避免空值错误--一类通常难以发现的错误。随着该版本的发布,我们鼓励软件包发布者开始将pub.dev上的共享软件包迁移到null safety。
我们非常高兴地看到null safety被采用的速度之快 仅仅在推出几个月后,pub.dev上最受欢迎的前500个软件包中的93%已经支持null safety。我们要对所有软件包的开发者表示衷心的感谢,感谢他们这么快就完成了这项工作,并帮助整个生态系统向前发展!
有了这么多支持null safety的软件包,你很有可能开始将你的应用程序迁移到使用null safety。第一步是使用dart pub outdated来检查你的应用程序的依赖性。详情请见null safety迁移指南。我们还修改了dart创建和flutter创建模板,使它们现在在新的应用和包中默认启用null safety。
宣布类型别名
类型别名是2.13语言的一个新特性。它扩展了我们早期的支持,即允许创建函数类型的类型别名,但不包括任何其他类型。这个备受追捧的功能是语言问题跟踪器中第二高的评价。
使用一个类型别名,你可以为任何现有的类型创建一个新的名字,然后可以在任何可以使用原始类型的地方使用。你并没有真正定义一个新的类型,只是引入了一个简短的别名。这个别名甚至可以通过类型平等测试。
typedef Integer = int;
void main() {
print(int == Integer); // true
}
那么你可以用类型别名做什么呢?一个常见的用途是给一个类型起一个更短或更具描述性的名字,使你的代码更容易阅读和维护。
一个很好的例子是处理JSON(感谢GitHub用户Levi-Lesches提供这个例子)。在这里我们可以定义一个新的类型别名Json,它将JSON文档描述为一个从String键到任何值的映射(使用动态类型)。然后我们可以在定义fromJson命名的构造函数和json获取器时使用这个Json类型别名。
typedef Json = Map<String, dynamic>;
class User {
final String name;
final int age;
User.fromJson(Json json) :
name = json['name'],
age = json['age'];
Json get json => {
'name': name,
'age': age,
};
}
你也可以在一个命名为类的类型别名上调用构造函数,所以下面的做法是完全合法的。
main() {
var j = Json();
j['name'] = 'Michael';
}
通过使用类型别名来给复杂的类型命名,你可以让读者更容易理解你的代码的不变性。例如,下面的代码定义了一个类型别名来描述包含通用类型X的键和List<X>
类型的值的地图。通过给类型一个带有单个类型参数的名称,地图的规则结构对代码的读者来说变得更加明显。
typedef MapToList<X> = Map<X, List<X>>;
void main() {
MapToList<int> m = {};
m[7] = [7]; // OK
m[8] = [2, 2, 2]; // OK
for (var x in m.keys) {
print('$x --> ${m[x]}');
}
}
=>
7 --> [7]
8 --> [2, 2, 2]
如果你试图使用不匹配的类型,你会得到一个分析错误。
m[42] = ['The', 'meaning', 'of', 'life'];
=>
The element type 'String' can't be assigned to the list type 'int'.
你甚至可以在公共库中重命名类时使用类型别名。想象一下,你在公共图书馆中有一个现有的类PoorlyNamedClass,你想把它重命名为BetterNamedClass。如果你简单地重命名这个类,那么你的API客户会突然出现编译错误。通过类型别名,你可以继续做重命名,但是要为旧的类名定义一个新的类型别名,然后为旧名添加@Deprecated注释。在使用PoorlyNamedClass时,会引起警告,但会继续编译并像以前一样工作,给用户时间来升级他们的代码。 下面是你如何实现BetterNamedClass并废除PoorlyNamedClass的方法(在一个名为mylibrary.dart的文件中)。
class BetterNamedClass {...}
@Deprecated('Use BetterNamedClass instead')
typedef PoorlyNamedClass = BetterNamedClass;
下面是有人试图使用PoorlyNamedClass时的情况。
import 'mylibrary.dart';
void main() {
PoorlyNamedClass p;
}
=>
'PoorlyNamedClass' is deprecated and shouldn't be used. Use BetterNamedClass instead.
类型别名功能从Dart 2.13开始可用。要启用它,请在你的pubspec中把Dart SDK的下级约束设置为至少2.13。
environment:
sdk: ">=2.13.0 <3.0.0"
这个功能是向后兼容的,这要归功于语言版本划分。2.13版本下SDK约束较低的包可以安全地引用2.13版本包中定义的类型别名,尽管2.13之前的包不能定义自己的类型别名。
Dart 2.13 FFI的变化
我们在Dart FFI中也有一些新功能,这是我们调用C代码的互操作机制。
首先,FFI现在支持有内联数组的结构(#35763)。考虑一个带有内联数组的C结构,像这样。
struct MyStruct {
uint8_t arr[8];
}
现在你可以直接用Dart包装,用Array的类型参数来指定元素类型。
class StructInlineArray extends Struct {
@Array(8)
external Array<Uint8> arr;
}
第二,FFI现在支持打包结构(#38158)。通常情况下,结构体在内存中的布局是为了让成员落在CPU更容易访问的地址边界上。在打包结构中,部分填充被省略,以降低整体内存消耗,通常是以特定平台的方式。通过新的@Packed(<alignment>)
注解,你可以轻松地指定填充。例如,下面的代码创建了一个在内存中具有4字节对齐的结构。
@Packed(4)
class TASKDIALOGCONFIG extends Struct {
@Uint32()
external int cbSize;
@IntPtr()
external int hwndParent;
@IntPtr()
external int hInstance;
@Uint32()
external int dwFlags;
...
}
Dart 2.13的性能变化
我们正在继续努力减少Dart代码的应用大小和内存占用。在大型Flutter应用程序中,代表AOT编译的Dart程序元数据的内部结构可能会占据相当大的内存块。这些元数据的存在是为了实现热重载、交互式调试和人类可读堆栈痕迹的格式化等功能,这些功能在部署的应用程序中从未使用过。在过去的一年里,我们一直在重组Dart本地运行时,以尽可能地消除这种开销。其中一些改进适用于在发布模式下构建的所有Flutter应用程序,但也有一些需要你通过使用--split-debug-info标志将调试信息从AOT编译的应用程序中分割出来,从而放弃人类可读的堆栈跟踪。
Dart 2.13包括一些变化,在使用--split-debug-info时,大大减少了程序元数据占用的空间。以Flutter Gallery应用程序为例。在Android上,发布的APK是112.4MB,包含调试信息,而不包含106.7MB(总体减少5%)。这个APK包含大量的资产。仅看APK内的代码元数据,它从Dart 2.12的5.7MB减少到Dart 2.13的只有3.7MB(减少35%)。
如果应用程序的大小和内存占用对你来说很重要,可以考虑使用--split-debug-info标志来省略调试信息。请注意,这样做的时候,你需要使用symbolize命令,使堆栈痕迹重新成为可读的人类。
官方Docker支持和谷歌云上的Dart
Dart现在可以作为Docker官方图像使用。虽然Dart已经提供了多年的Docker镜像,但这些新的Dart镜像是由Docker测试和验证的,遵循最佳实践。它们还支持提前编译(AOT),这可以大大减少构建的容器的大小,并可以提高容器环境中的部署速度--比如Cloud Run。
虽然Dart仍然专注于使Flutter等应用框架能够在每个屏幕上驱动美丽的像素,但我们意识到,大多数用户体验的背后至少有一个托管服务。通过让Dart轻松构建后端服务,我们支持全栈体验,让开发者使用与前端小部件相同的语言和业务逻辑,将他们的应用扩展到云端。
一般来说,将Dart用于Flutter应用程序的后端,特别适合谷歌管理的无服务器平台Cloud Run的简单性和可扩展性。这包括零扩展,这意味着当你的后端不处理任何请求时,你不会产生成本。我们与谷歌云团队合作,提供Dart的函数框架,这是一个软件包、工具和例子的集合,可以轻松地编写Dart函数来部署,而不是处理HTTP请求和CloudEvents的完整服务器。
请查看我们的谷歌云文档以开始使用。
关于下一步的工作,请说几句
我们已经在为即将发布的版本做一些令人兴奋的改变。一如既往,你可以使用语言漏斗跟踪器来关注我们的进展。
我们正在努力的一个领域是为Dart和Flutter提供一套新的规范线。Lints是配置Dart静态分析的一种强大方式,但由于有数百种可能的lints可以开启或关闭,很难决定选择什么。我们目前正在努力定义两套典型的衬垫,我们将在Dart和Flutter项目中默认应用。我们希望在下一个稳定版中默认启用。如果你想预览一下,请查看lints和flutter_lints这两个包。
最后,如果你对Dart VM运行时进行深度嵌入,请注意,我们正计划废除现有的机制。我们将用一个基于Dart FFI的更快、更灵活的模型来取代它(见追踪问题#45451)。
Dart 2.13现在已经推出
带有类型别名和改进的FFI的Dart 2.13今天可以在Dart 2.13和Flutter 2.2 SDKs中使用。 如果你一直在等待你的依赖关系迁移到null safety,你可能想再次检查,使用dart pub outdated。在前500个最受欢迎的软件包中,有93%已经迁移了,所以你很有可能已经解禁了。我们也要向已经迁移的开发者表示感谢!我们很想听听你们的意见。 我们很想听听你对这篇博文中讨论的新功能和变化的体验。请在下面留言或发推特@dart_lang。
通过www.DeepL.com/Translator(免费版)翻译
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!