乐趣区

关于dart:Dart-212-现已发布

作者 / Michael Thomsen

Dart 2.12 现已公布,其中蕴含 健全的空平安 和 Dart FFI 的稳定版。空平安是咱们最新主打的一项生产力强化性能,意在帮忙您躲避空值谬误,以前这种谬误通常很难被发现,您能够观看上面这支视频理解详情。FFI 则是一种互操作机制,反对调用以 C 语言编写的既有代码,例如调用 Windows Win32 API。欢送大家即刻开始应用 Dart 2.12。

https://www.bilibili.com/vide…

Dart 平台的独特性能

在具体理解健全空平安和 FFI 之前,咱们先来讨论一下它们在哪些方面符合了咱们对 Dart 平台的冀望。编程语言往往有很多相似的性能,例如,很多语言都反对面向对象的编程或在 web 上运行。真正将各个语言辨别开来的,是其独特的性能组合。

Dart 具备横跨三个维度的独特性能组合:

  • 可移植性: 高效的编译器可针对设施生成 x86 和 ARM 机器代码,并针对 web 生成优化的 JavaScript。同时兼容挪动设施、桌面 PC、利用后端等多种 指标平台。大量的开发库和 package 提供了可在所有平台上应用的统一的 API,进一步升高了开发者创立真正多平台利用的老本。
  • 高生产力: Dart 平台反对热重载,因而可在原生设施和 web 上实现疾速迭代开发。此外,Dart 还提供了丰盛的构造,如 isolates 和 async/await 等,用以解决和实现常见的并发和事件驱动的利用模式。
  • 持重: Dart 的健全空平安类型零碎能够在开发过程中就捕捉到谬误。整个平台领有极好的可扩展性和可靠性,曾经被大量且多样的利用在累计超过十年的生产环境中实战测验过,其中包含 Google 的一些要害业务利用,如 Google Ads 和 Google Assistant 等。

健全空平安加强了类型零碎的稳健性,同时进步了性能。借助 Dart FFI,您能够取得更强的可移植性,同时沿用由 C 语言编写的既有代码,在解决对性能要求极为严苛的工作时,能够纵情应用通过精心优化的 C 语言代码。

健全的空平安

自 Dart 2.0 中引入健全类型零碎以来,Dart 语言中最重大的新增内容便是健全空平安。空平安进一步加强了类型零碎,让您可能捕捉到空值谬误,此类谬误常常导致利用解体。启用空平安后,您就能够在开发过程中捕捉到空值谬误,防止利用在生产环境中产生解体。

健全空平安的设计围绕一套外围准则开展。您能够浏览 官网文档 理解这些准则对开发者的影响。

默认不可空: 从基本扭转类型零碎

在空平安呈现之前,开发者面临的外围挑战在于无奈辨别预期收到空值的代码和不承受空值的代码。几个月前,咱们在 Flutter 的 master 渠道中发现了一个谬误,多个 flutter 工具命令在特定计算机配置下会产生解体,并触发空值谬误: The method '>=' was called on null。问题出自如下代码:

final int major = version?.major;
final int minor = version?.minor;
if (globals.platform.isMacOS) {
 // plugin path of Android Studio changed after version 4.1.
 if (major >= 4 && minor >= 1) {...

您发现错误了吗?因为 version 可能为空,所以 majorminor 也可能为空。如果独自查看此处代码,这一谬误仿佛并不难发现。但实际上,即便通过了严格的代码审查过程 (如 Flutter repo 所采纳的代码审查流程),也总是不免有这样的漏网之鱼。在启用空平安后,动态剖析可能立刻捕捉到这一问题 (如下图)。您能够 在 DartPad 中亲自上手体验。

△ IDE 中的剖析后果

这只是一个非常简单的谬误。咱们晚期在 Google 外部的代码中应用空平安时,捕捉到的简单谬误远多于此。其中一些是多年前就曾经发现的 bug,但在通过空平安进行额定的动态查看前,很多团队都未能找到起因。

  • 外部团队发现,他们常常查看表达式中是否存在空值,而这些表达式永远不可能为空。这个问题在应用 protobuf 的代码中最常见,其中可选字段在未经设置时会返回一个默认值,而且永不为空。这会导致代码混同默认值和空值,并谬误地查看默认条件。
  • Google Pay 团队在他们的 Flutter 代码中发现了一些 bug,在尝试拜访 Widget 上下文之外的 Flutter State 对象时会出错。在采纳空平安之前,这些对象会返回 null 并覆盖谬误;在采纳空平安之后,健全剖析确定这些属性永远不可能为空,并会给出剖析谬误。
  • Flutter 团队发现了一个 bug: 如果在 Window.render() 中向 scene 参数传递空值,则 Flutter 引擎可能会解体。在向空平安迁徙的过程中,他们增加了一个提醒,将 Scene 标记为不可空,即可轻松避免空值可能引发的利用解体。

在默认不可空的前提下工作

启用空平安 后,申明变量的根底办法会发生变化,因为默认类型不可为空:

// 在空平安的 Dart 中,以下均不可为空
var i = 42; // Inferred to be an int.
String name = getFileName();
final b = Foo();

如果您想要创立可能同时蕴含值或 null 的变量,则须要在申明变量时在类型前面显式增加 ? 后缀:

// aNullableInt 能够为整型或 null
int? aNullableInt = null;

空平安的实现很持重,并提供丰盛的动态流程剖析,不便开发者轻松解决可空类型。例如,局部变量在进行空值查看后,Dart 会将其类型从可空晋升为非空:

int definitelyInt(int? aNullableInt) {if (aNullableInt == null) {return 0;}
 // aNullableInt 当初会被提醒为非空 int
 return aNullableInt;
}

咱们还增加了一个新的关键字,required。当一个命名的参数被标记为 required (在 Flutter widget API 中经常出现),而调用者遗记提供该参数时,就会产生如下剖析谬误:

渐进迁徙至空平安

空平安对于咱们的类型零碎而言是一项根本性的扭转,因而如果咱们执意强制所有开发者采纳,势必会造成重大的凌乱。因而,咱们想让 您自行决定 适合的迁徙机会,空平安将是一项可选个性: 在做好筹备之前,您能够在无需强制启用空平安的状况下应用 Dart 2.12。您甚至能够在尚未启用空平安的利用或 package 中依赖已启用空平安的 package。

为了帮忙您将现有代码迁徙至空平安,咱们提供了迁徙工具和 迁徙指南。该工具会首先剖析您所有的代码,而后您能够交互式地查看工具推断出的可空属性,如果您不批准工具得出的论断,则能够增加可空性提醒以更改推断。增加迁徙提醒可能会大幅晋升迁徙品质。

目前,在默认状况下,应用 dart create 和 flutter create 新创建的 package 和利用中不会启用健全空平安。在大部分生态系统实现迁徙后,咱们预计将在后续的稳固版本中默认启用。您能够通过 dart migrate 在新创建的 package 或利用中轻松 启用空平安。

Dart 生态系统的空平安迁徙状态

去年,咱们提供了健全空平安的数个预览版和 Beta 版,旨在为生态系统提供首批反对空平安的 package。这项工作十分重要,咱们倡议大家 有序迁徙至健全空平安,也就是说,在所有依赖项迁徙实现之前,最好不要迁徙本人的 package 或利用。

咱们已公布由 Dart、Flutter、Firebase 和 Material 团队所提供的数百个 package 的空平安版本。令人惊喜的是,Dart 和 Flutter 生态系统对此也予以微小的反对,pub.dev 当初共有 1,000 多个 package 反对空平安。而且重要的是,最受欢迎的 package 已率先实现迁徙,截止到 Dart 2.12 公布时,前 100 个最受欢迎的 package 中已有 98 个反对空平安,而在前 250 和前 500 的 package 中,反对空平安的比例则为 78% 和 57%。咱们心愿在接下来的几周,pub.dev 上可能呈现更多反对空平安的 package。咱们的剖析 表明,pub.dev 上的绝大多数 package 曾经能够 开始迁徙。

充沛健全的空平安的劣势

实现迁徙后,您的我的项目就处于健全的空平安模式下了。这意味着 Dart 可能齐全确保具备不可空类型的表达式不为空。当 Dart 剖析完您的代码并确定某个变量不可为空时,该变量将始终不可为空。Dart 与 Swift 都领有健全的空平安,但有些编程语言在这方面仍有待改良。

Dart 的健全空平安还暗含另一项备受期待的劣势: 您的程序能够更小、更快。因为 Dart 可能确保不可为空的变量绝不为空,因而能够 实现优化。例如,Dart 的运行前 (ahead-of-time, AOT) 编译器能够生成更小更快的原生代码,因为当其晓得变量不为空时,便不再须要增加空值查看了。

Dart FFI: 集成 Dart 与 C 语言代码库

您能够通过 Dart FFI 调用 C 语言编写的既有代码库,从而加强可移植性,还能够通过精心打磨的 C 代码实现对性能要求极为严苛的工作。从 Dart 2.12 起,Dart FFI 已完结 Beta 测试阶段,现已进入稳固状态,能够用于生产环境。咱们还增加了一些新性能,包含嵌套构造和按值传递构造。

按值传递构造

在 C 语言中,构造可通过援用和值进行传递。FFI 以前仅反对按援用传递构造,但从 Dart 2.12 开始,也反对按值传递。下方的简略示例中,两个 C 函数应用援用和值实现传递:

struct Link {
  double value;
  Link* next;
};

void MoveByReference(Link* link) {link->value = link->value + 10.0;}

Coord MoveByValue(Link link) {
  link.value = link.value + 10.0;
  return link;
}

嵌套构造

C API 通常应用嵌套构造,这种构造自身也蕴含构造,比方以下示例:

struct Wheel {int spokes;};

struct Bike {
 struct Wheel front;
 struct Wheel rear;
 int buildYear;
};

从 Dart 2.12 起,FFI 将反对嵌套构造。

API 改变

作为 FFI 稳定版公布内容的一部分,并且为了反对上述性能,咱们做了一些小幅的 API 改变。

当初不容许创立空构造 (重要改变参照 #44622),并会给出弃用正告。您能够应用一个新的类型 Opaque 来示意空构造。dart:ffi 函数 sizeOf、elementAt 和 ref 当初须要编译时的类型参数 (重要改变参照 #44621)。因为在 package:ffi 中减少了新的便当函数,所以在常见的状况下,无需额定增加对于调配和开释内存的模板代码:

// 调配一个 Utf8 数组,应用 Dart 字符串填充,而后传递给 C 办法并转换后果,最初开释 arg
//
// API 变更前:final pointer = allocate<Int8>(count: 10);
free(pointer);
final arg = Utf8.toUtf8('Michael');
var result = helloWorldInC(arg);
print(Utf8.fromUtf8(result);
free(arg);
// API 变更后:final pointer = calloc<Int8>(10);
calloc.free(pointer);
final arg = 'Michael'.toNativeUtf8();
var result = helloWorldInC(arg);
print(result.toDartString);
calloc.free(arg);

主动生成 FFI 绑定

对于大型的 API 接口,编写与 C 代码集成的 Dart 绑定极其耗时。为加重这一累赘,咱们为大家筹备了绑定生成器,能够通过 C 头文件主动创立 FFI 封装代码,欢送试用。

FFI 路线图

外围 FFI 平台实现后,咱们的工作重心将转向基于外围平台扩大 FFI 功能集。咱们正在钻研的一些性能包含:

  • ABI 特定数据类型,如 int、long、size_t (#36140)
  • 构造中的内联数组 (#35763)
  • Packed 构造 (#38158)
  • 联结类型 (#38491)
  • 对 Dart 凋谢终结办法 (finalizer) (#35770,请留神,您当初能够通过 C 语言应用终结办法)

FFI 应用示例

在过来的几个月中,咱们看到大家在应用 Dart FFI 集成一系列基于 C 语言的 API 时,发掘出了许多有创意的用法。上面介绍几个示例:

  • open_file 是一个用于在多个平台关上文件的 API,应用 FFI 在 Windows、macOS 和 Linux 上调用操作系统原生 API。https://pub.flutter-io.cn/pac…
  • win32 封装了最罕用的 Win32 API,便于从 Dart 间接调用各种 Windows API。
  • objectbox 是一个疾速数据库,底层由 C 语言实现。
  • tflite_flutter 应用 FFI 封装了 TensorFlow Lite API。

Dart 语言的将来打算

健全空平安是这几年咱们对 Dart 语言做出的最大扭转。接下来,咱们将持续稳步改良 Dart 语言和平台。上面简略介绍一些咱们在 语言设计规划 中试验的内容:

  • 类型别名 (#65): 将创立类型别名的性能扩大到非函数类型。例如,您能够创立一个 typedef 并将其用作变量类型:
typedef IntList = List<int>;
IntList il = [1,2,3];
  • 无符号右移运算符 (#120): 增加新的 >>> 运算符,便于对整数执行无符号移位操作。此运算符可齐全重写。
  • 通用元数据注解 (#1297): 扩大元数据注解,以同时反对含类型参数的注解。
  • 动态元编程 (#1482): 反对动态元编程,即编译期间生成新 Dart 源代码的 Dart 程序,与 Rust 宏 (macro) 和 Swift 函数构建器 (function builder) 相似。该性能目前仍处于晚期摸索阶段,但咱们认为它可能会开启全新用例的大门,突破当初依赖代码生成的僵局。

Dart 2.12 现已公布

欢送大家下载 Dart 2.12 和 Flutter 2.0 SDK,即刻开始应用 Dart 2.12,纵情体验健全空平安和稳定版 FFI。请大家阅览 Dart 和 Flutter 的已知空平安问题。如果您发现其余任何问题,请在 Dart 问题跟踪页 中报告给咱们。

如果您已在 pub.dev 上公布了 package,请立刻参阅 迁徙指南,理解如何迁徙至健全空平安。迁徙有助于依赖您的 package 的其余 package 和利用实现迁徙。咱们在此向曾经实现迁徙的开发者们表示感谢!

欢送大家与咱们分享本人的健全空平安和 FFI 体验,咱们评论区见!

退出移动版