做者 / Michael Thomsenhtml
Dart 2.12 現已發佈,其中包含 健全的空安全 和 Dart FFI 的穩定版。空安全是咱們最新主打的一項生產力強化功能,意在幫助您規避空值錯誤,之前這種錯誤一般很難被發現,您能夠觀看下面這支視頻瞭解詳情。FFI 則是一種互操做機制,支持調用以 C 語言編寫的既有代碼,例如調用 Windows Win32 API。歡迎你們即刻開始使用 Dart 2.12。git
https://www.bilibili.com/vide...github
在詳細瞭解健全空安全和 FFI 以前,咱們先來討論一下它們在哪些方面契合了咱們對 Dart 平臺的指望。編程語言每每有不少相似的功能,例如,不少語言都支持面向對象的編程或在 web 上運行。真正將各個語言區分開來的,是其獨特的功能組合。web
Dart 具備橫跨三個維度的獨特功能組合:數據庫
健全空安全加強了類型系統的穩健性,同時提升了性能。藉助 Dart FFI,您能夠得到更強的可移植性,同時沿用由 C 語言編寫的既有代碼,在處理對性能要求極爲嚴苛的任務時,能夠盡情使用通過精心優化的 C 語言代碼。編程
自 Dart 2.0 中引入健全類型系統以來,Dart 語言中最重大的新增內容即是健全空安全。空安全進一步加強了類型系統,讓您可以捕捉到空值錯誤,此類錯誤常常致使應用崩潰。啓用空安全後,您就能夠在開發過程當中捕捉到空值錯誤,避免應用在生產環境中發生崩潰。swift
健全空安全的設計圍繞一套核心原則展開。您能夠閱讀 官方文檔 瞭解這些原則對開發者的影響。後端
在空安全出現以前,開發者面臨的核心挑戰在於沒法區分預期收到空值的代碼和不接受空值的代碼。幾個月前,咱們在 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
可能爲空,因此 major
和 minor
也可能爲空。若是單獨檢查此處代碼,這一錯誤彷佛並不難發現。但實際上,即便通過了嚴格的代碼審查過程 (如 Flutter repo 所採用的代碼審查流程),也老是不免有這樣的漏網之魚。在啓用空安全後,靜態分析可以當即捕捉到這一問題 (以下圖)。您能夠 在 DartPad 中親自上手體驗。安全
△ IDE 中的分析結果
這只是一個很是簡單的錯誤。咱們早期在 Google 內部的代碼中使用空安全時,捕捉到的複雜錯誤遠多於此。其中一些是多年前就已經發現的 bug,但在經過空安全進行額外的靜態檢查前,不少團隊都未能找到緣由。
啓用空安全 後,聲明變量的基礎方法會發生變化,由於默認類型不可爲空:
// 在空安全的 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 或應用中輕鬆 啓用空安全。
去年,咱們提供了健全空安全的數個預覽版和 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 調用 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 將支持嵌套結構。
做爲 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);
對於大型的 API 接口,編寫與 C 代碼集成的 Dart 綁定極其耗時。爲減輕這一負擔,咱們爲你們準備了綁定生成器,能夠經過 C 頭文件自動建立 FFI 封裝代碼,歡迎試用。
核心 FFI 平臺完成後,咱們的工做重心將轉向基於核心平臺擴展 FFI 功能集。咱們正在研究的一些功能包括:
在過去的幾個月中,咱們看到你們在使用 Dart FFI 集成一系列基於 C 語言的 API 時,發掘出了許多有創意的用法。下面介紹幾個示例:
健全空安全是這幾年咱們對 Dart 語言作出的最大改變。接下來,咱們將繼續穩步改進 Dart 語言和平臺。下面簡單介紹一些咱們在 語言設計規劃 中實驗的內容:
typedef IntList = List<int>; IntList il = [1,2,3];
歡迎你們下載 Dart 2.12 和 Flutter 2.0 SDK,即刻開始使用 Dart 2.12,盡情體驗健全空安全和穩定版 FFI。請你們閱覽 Dart 和 Flutter 的已知空安全問題。若是您發現其餘任何問題,請在 Dart 問題跟蹤頁 中報告給咱們。
若是您已在 pub.dev 上發佈了 package,請當即參閱 遷移指南,瞭解如何遷移至健全空安全。遷移有助於依賴您的 package 的其餘 package 和應用完成遷移。咱們在此向已經完成遷移的開發者們表示感謝!
歡迎你們與咱們分享本身的健全空安全和 FFI 體驗,咱們評論區見!