1. 程式人生 > 實用技巧 >用 Dart 來寫 Objective-C 程式碼

用 Dart 來寫 Objective-C 程式碼

問題背景

先說說為什麼會有開發效率的問題。Flutter 的跨平臺多適用於 UI 等上層需求,本來是可以提升開發效率的。但是諸如 LBS、系統和裝置資訊、獲取相簿等常用功能都需要兩端去寫很多 Native 程式碼。最終原本的『兩端開發』最後成了『三端開發』。很少會有完全用 Flutter 開發的 App,原因如下:

  1. 一些跟系統和裝置強相關的功能只能靠呼叫 API 來實現
  2. 舊專案引入 Flutter 後需呼叫已有的 Native 模組程式碼

既然『三端開發』無法避免,那麼增加了哪些成本呢?

  1. 開發過程中需要在至少兩個 IDE 開啟的工程中來回切換,需單獨執行,無論是寫程式碼還是 Debug 都體驗不連貫,降低效率
  2. 如果 Flutter 和 Native 程式碼由不同的人來開發和維護,增加了溝通成本
  3. Flutter 需要通過編寫 channel 程式碼來與 Native 層互動,需要兩端開發時統一資料傳輸協議。不僅 channel 呼叫效能較差,Model資料在 Native 與 Flutter 之間傳遞過程的序列化和反序列化也降低效能。
  4. 通過 channel 在 Flutter 和 Native 之間呼叫時只支援非同步回撥

分析問題

既然無法避免呼叫 Native 的 API,那麼就要面對這個事實。下一步是如何能讓呼叫 Native API 的這個過程效率更高。具體體現如下:

  1. 開發效率提高:直接用 Dart 語言在 Flutter 工程裡編寫和除錯程式碼,無需切換到 Xcode 等其他 IDE 開啟的 Native 工程
  2. 執行效率提高:channel 的呼叫效能差一直被詬病

所以思路就是:

  1. 將 Native API 封裝成對應的 Dart 語言,解決一系列語言之間的型別轉換和語法相容問題
  2. 通過一個更高效的方式來呼叫 Native API,這裡使用 dart:ffi 呼叫 C函式,再通過 Runtime 機制呼叫 Native

廣州品牌設計公司https://www.houdianzi.com

使用方法

假如你寫了個 Objective-C 的類叫 RuntimeStub,並實現了個 fooBlock:方法,引數和返回值都是個 block 物件。

@interface RuntimeStub ()
@end
@implementation RuntimeStub
typedef int(^BarBlock)(NSObject *a);
- (BarBlock)fooBlock:(BarBlock)block {
    ...
}
@end

利用dart_objc寫 Dart 程式碼呼叫過程如下:

初始化一個 NSObject物件,傳入類名就可以 new任意型別的物件。 perform()方法可以呼叫任意物件的任何方法,跟 Objective-C 的用法基本一致。

NSObject stub = NSObject('RuntimeStub');
Block block = stub.perform(Selector('fooBlock:'), args: [barFunc]);

Objective-C 中 Block 這種匿名函式或閉包的概念在 Dart 中其實就是 Function,所以當引數是 Block 物件的時候,可以直接傳入一個與之函式簽名一樣的 Dart Function 物件。dart_objc會自動完成引數型別轉換和呼叫等一系列底層細節。所以用 Dart 實現的 barFunc與 Objective-C 介面BarBlock的簽名需要一致:

Function barFunc = (NSObject a) {
    print('hello block! ${a.toString()}');
    return 101;
};

Dart 呼叫 Block 也很簡單,呼叫 invoke方法就行:

int result = block.invoke([stub]);

最後也可以用 Dart 封裝下 RuntimeStub類,這樣呼叫程式碼更簡潔。這種模板程式碼後續會做成自動生成的,而不用手寫。

class RuntimeStub extends NSObject {
  RuntimeStub() : super('RuntimeStub');

  Block fooBlock(Function func) {
    return perform(Selector('fooBlock:'), args: [func]);
  }
}

後續

由於dart_objc元件還在基於 dev 版本的 Dart 開發,可能後續還會有比較大的變動,甚至是 API 的變化。所以沒有過多展開講實現細節,感興趣可以去自己看程式碼:https://github.com/yulingtianxia/dart_objc

目前的 Cocoa API 封裝打算參考 Swift 版本的文件,畢竟 Dart 有些語法跟 Swift 還有點像。

Android 平臺的實現也在規劃中,最終將會結束 Flutter 三端開發現狀,實現真正的前端大一統。