写点什么

Dart vs Swift

用户头像
柠檬水
关注
发布于: 2020 年 06 月 28 日
Dart vs Swift

前言

Dart、Swift 目前是开发用的比较多的语言,他们都是比较现代化的语言,在语法方面都比较像,本文会对其语言进行一下对比,便于记忆和语言过渡。本次的对比是基于 Dart 2.8.3 和 Swift 5.2 的版本进行。

预览

(😅居然不支持 Markdown 表格)


变量声明

Swift  中语法如下:

var name:Stringvar age:Intvar height:Double
//赋值语法var name = "Summer"var age = 6var height = 1.12
复制代码


Dart 中语法如下:

String name;int age;double height;
//赋值语法var name = "Summer";var age = 6;var height = 1.12;
复制代码

由于两种语言都有类型推断,所以可以通过表达式中的值推断出类型。 Dart 需要增加分号, Swift 中可以不加分号(根据习惯你也可以加)。

更多类型,对比下文 Swift 会更严格一些对于混合型的需要加 Any 表明,个人其实也同意 Swift 的做法,如果类型为 Any 意味着你将失去编译器的类型检查机制,隐藏的挖坑。对于动态类型 Swift 使用 Any 关键词,而 Dart 使用 dynamic 。


  • Swift 

var mapArgsMix = ["arg1":"hello", "arg2":"world", "arg3":2020] as [String:Any]var listArgsMix = ["hello","world",2020] as [Any]var mapArgs = ["arg1":"hello", "arg2":"world"] //[String:String]var listArgs = ["hello","world"]  //[String]
复制代码


  • Dart 

var maxArgs = {"arg1":"hello", "arg2":"world","arg3":2020} // Map<String,Object>var listArgs = ["hello","world",2020] //List<Object>
复制代码

Swift 的类型扩展阅读链接,Dart 的在此链接


可变与不可变变量声明

我们编程过程经常用到可变变量与不可变变量,两者都可以通过 var 来声明可修改变量,声明不可变变量的语法有所不同。


  • Swift

var age = 60age = 18
复制代码


  • Dart

var age = 60;age = 18
复制代码

对于 Swift 使用 let 关键字, Dart 使用 final 和 const 来声明。 let 和 final 的作用相似,都可以声明不可变变量,而且 Swift 的声明非可选类型和 Dart 的 final 声明的变量必须初始化,否则会编译报错。 Dart 的 const 是编译时常量,相对 final 会增加严格,如同是 final 和 const 声明的数组,前者声明的数组变量内部可以修改,但是 const 声明的数组内部也不允许修改,而 Swift 用 let 声明的数组编译时就不允许访问下标,想一想 NSArray  和 NSMutaleArray 关系就理解了。


  • Swift

let age = 60age = 18 //Cannot assign to value: 'age' is a 'let' constant
let children = ["summer","winter"]children[0] = "spring" //Cannot assign through subscript: 'children' is a 'let' constant
复制代码


  • Dart

final age = 60;age = 18; //'age' can't be used as a setter because it's final.
final child = ["summer","winter"];child[0] = "Spring";
const balls = ["football","basketball"];balls[0] = ["ping-pong"];// 运行时报错 UnsupportedError (Unsupported operation: Cannot modify an unmodifiable list)
复制代码


函数声明

Swift 和 Dart 中函数都是一等公民,因此都可以作为参数传递,变量声明,作为返回值等。


  • Swift

func foo()func bar() -> Int
复制代码


  • Dart

void foo();int bar();
复制代码

查看关于函数的介绍更多: SwiftDart

参数命名与非命名参数

Swift 和 Dart 都支持参数命名和非参数命名的方式传参,两种实现方式都很友好。看几个例子。


  • Swift

//带参数名的传参func foo(name: String, age: Int, height: Double)foo(name: "summer", age: 6, height: 1.12)
//不带参数传参func foo(_ name: String,_ age: Int,_ height: Double)foo("summer",16,1.12)
复制代码


  • Dart

//带参数名的传参void foo({String name, int age, double height});foo(name: "summer", age: 6, height: 1.12);foo(age: 6,name: "summer",height: 1.12);//支持参数顺序变换
//不带参数传参void foo(String name, int age, double height); // 对比上面的函数去掉了大括号foo("summer",16,1.12);
复制代码

从调用的角度看都是一样的,Dart 还有个好用的语法糖。如 foo({ this.name, this.age, this.height }) ,可以保证传参名字和内部变量名称相同。

函数参数可选和默认值

两者都支持参数可选,也支持设置默认值。设置默认值后可以通过传参时指定值覆盖掉默认值,提供这个语法很大的好处是读代码的时候可以一目了然,那些值不传的话默认值会不会设置。


  • Swift

func foo(name: String, age: Int = 0, height: Double = 0.8)foo(name:"spring",age:3)
复制代码


  • Dart

void foo(String name, [int age = 0, double height = 0.8]);//指定顺序void foo(String name, {int age = 0, double height = 0.8});//同上void foo({String name, int age = 0, double height = 0.8});//不指定顺序
foo(name:"spring",age:3);
复制代码


更多阅读查看这里 Swift 文档, Dart 的文档

闭包

由于函数作为一等公民,因此可以做为参数进行传递,对比 Java,Swift 和 Dart 对闭包的支持更加强大,高阶函数的提出,其实也是对编程的函数高阶抽象。


  • Swift

let list = ["car","bike","motorcycle"]list.forEach { (item) in    print("this transport is \(item)")}
//或者如下list.forEach({print("this transport is \($0)")})
复制代码

在 Swift 中可以用 $0 、 $1 代替第几个参数,可以简化写法

  • Dart

final list = ["car","bike","motorcycle"];
list.forEach((element) {print("this transport is $element");});//或者list.forEach((element)=>print("this transport is $element"));
复制代码

这里如果直接 return 的匿名函数写法可以简化为 ()=>dosomething 闭包经常用在异步编程中作为回调参数,我们将会在异步编程这一节详细介绍。

查看更多:Swift 闭包介绍,Dart 异步编程

元组(Tuple)

元组在函数返回多个值的时候非常有帮助, Dart 目前还不支持,有第三方库的实现,但是不推荐,可读性没有 Swift 的元组清晰,建议返回定义的数据结构解决这个问题。


  • Swift

let tpHasName = (max: 3, min: 0)//推荐写法print("max is \(tpHasName.max) and min is \(tpHasName.min)")
let tpNoName = (3,0)print("first is \(tpNoName.0) and second is \(tpNoName.1)")
复制代码


控制流

Swift 的 IF 条件和循环控制都还中规中矩,到 Switch 的时候就放飞自我了。Dart 相对来说没有那么多语法,相对简单。

循环


  • Swift

let list = ["car","bike","motorcycle"]for item in list {    print(item)}
for i in 0..<list.count { print(list[i])}
for i in 0...2 { print(list[i])}
var i = 0
while i < list.count { print(list[i]) i += 1;}
var j = 0repeat { print(list[j]) j += 1 }while j < list.count
复制代码


  • Dart

var list = ["car","bike","motorcycle"];
void loops(){ for(var item in list){ print(item); } for(int i=0;i<list.length;i++){ print(list[i]); }

var k = 0; do { print(list[k]); k++; } while (k < list.length);
var j = 0; while (j < list.length){ print(list[j]); j++; } }
复制代码


IF

条件的判断上 Swift 不用 () 来包含条件,这与 let 语法解包可能有一定关系。 Dart 采用的是传统的 () 包含条件。


  • Swift

var name:String? = "summer"if let n = name {	print(n)}
var num = 7if num > 10 { print("num is big than 10")} else if num < 5 { print("num is smaller than 5")} else { print("this num between 5 and 10")}
复制代码


  • Dart

var num = 7;
if (num > 10){ print("num is big than 10")} else if (num < 5){ print("num is smaller than 5")} else { print("this num between 5 and 10")}
复制代码


Switch

结合枚举 Swift 可以用 Switch 玩出各种特色,除此之外还可以 switch 字符,元组, case 可以增加多个条件,通过默认不需要写 break ,默认只执行匹配到第一个 case ,可以通过 fallthrough 像其他语言一样匹配下一个 case 。Dart 相对比较简单就是普通的语句。


  • Swift

var num = 8
switch num {case 0...9: print("we meet in 0..9") fallthroughcase 7..<10: print("we meet in >=7 && < 10")case 5,6,7,8: print("we meet in 5 6 7 8")default: print("we meet nothing") break;}

var tp = (x:0,y:10)
switch tp {case (_,10): print("y is match")case (let x, 10): print("y is match and x is \(x)")case let (x,y) where x == y : print("x is equal to y and x is \(x) y is \(y)")case let (x,y): print("x:\(x) and y:\(y) is normal")}
复制代码


  • Dart

    var lang = "ch";    switch (lang) {      case "ch":        print('language is ch');        break;      case "en":        print('language is en');        break;      default:        print("language is $lang");    }
复制代码


其他

Swift 中还有一些独特的关键字,如 defer , guard 查看官网文档吧。


集合类型

集合类型是非常常用的类型。

数组(list)


  • Swift

var emptyArray = [Int]()var list = [1, 2, 4]list.count // 3list[1] // 1
复制代码


  • Dart

var emptyArray = <int>[];var list = [1, 2, 4];list.length; // 3list[1] // 1
复制代码


集合(Sets)


  • Swift

var emptySets = Set<String>()var transpot = Set<String>(["car", "bike", "motorcycle"])
复制代码


  • Dart

var emptySets = <String>{};var transport = {"car", "bike", "motorcycle"};
复制代码


键值类型(Map)


  • Swift

var emptyMap = [String:Int]();var nameOfAge = ["summer" : 6, "spring" : 3];
复制代码


  • Dart

var emptyMap = Map<String,Int>();var emptyMap = <String,Int>();
var nameOfAge = {"summer" : 8, "spring" : 3};
复制代码

更多的介绍查看这里 Swift 集合类型,Dart 集合类型


空值和可选类型

在 Dart 访问 null 类型会产生空引用异常,Swift 自诞生开始就支持了 optional 类型,本来在运行时才能进行的检查,我们可以在编译期就支持空值的处理,Dart 目前也已经支持,截止到这片文章发布,该功能还处于 测试阶段,Dart 团队还不建议在生产环境使用。


  • Swift

var x:Int? //optionalvar y:Int = 1 //non-optional must be initializedvar z:Int? = 2
func foo(name:String?){ guard let name = name else { print("no value") return } print(name) print(z!)//如果确定不是null,可以使用 ! 解包}
foo(name: nil) //no valuefoo(name:"summer") //summer
复制代码


  • Dart

var int? x;var double? f = 1.3;
print(f!) //已知 f 不为空 可以直接解包print(x?.floor()) // 如果x 是null,则返回null,否者返回运行的结果
复制代码


可以看到除了声明方式有些不一样,其他方面都很像。其实这也说明一个问题,可选类型的发明是为了解决空引用的问题而诞生的,如果方式方法相同,本质是一样的解决方案。学习这些语言特性的时候多从设计语言的人的角度看看,就很容易理解,各种设计是为了解决问题而诞生的,不是为了存在而存在。更多阅读 Swift 可选类型链, Dart null-safety


类(Class)

类在两个语言中都有实现,语法有些区别。由于 Swift 中对可选类型的约定,如果是非可选类型必须在初始化的时候设置值,否则会编译不通过。比如下面代码除 name 之外其他的属性必须都设置值。


  • Swift

class Person{    var name:String?    let age:Int    let height:Double        init(age: Int, height: Double) {        self.age = age        self.height = height    }}
let p = Person(age: 6, height: 1.12)
复制代码


  • Dart

class Person{    Person(this.name,this.age,this.height);  final String name;  final int age;  final double height;}
复制代码

注意这里, this.[propertyname] 是 Dart 提供的语法糖,可以快速实现原始属性的赋值。

构造器功能扩展


  • Swift


Swift 结构体和类都支持构造器,Swift 中默认是不调用父类的 init 方法的,类还支持 convenience ,更多的内容查看 Swift 的 初始化(非常推荐)介绍。

class Human{    var legs:String?    var hands:String?    init(legs:String?,hands:String?) {        self.legs = legs        self.hands = hands    }
}
class Person:Human{ var name:String? let age:Int let height:Double override init(legs: String?, hands: String?) { //重写父类的初始化方法 self.age = 0; self.height = 0; super.init(legs: legs ?? "biglegs", hands: hands ?? "bighands");//初始化完自己后再调用父类 } init(name: String?, age: Int, height: Double) { //designated initializer self.name = name self.age = age self.height = height super.init(legs: "humanHands", hands: "humanLegs") //调用父类designated initializer self.hands = "bobyhands" //访问父类属性必须在初始化父类后再访问 } init(age:Int,height:Double){ //designated initializer self.age = age self.height = height super.init(legs: "humanHands", hands: "biglegs") } convenience init() { //convenience 方法 self.init(age:10,height:1.1) self.name = "noname" }}
复制代码


  • Dart


Dart 可以通过 facory 关键词声明一个构造器不一定返回一个新的实例回来,比如单例或者来自 Cache 。

class SingletenSimple{
static get instance => _getInstance(); factory SingletenSimple() => _getInstance(); factory SingletenSimple.single() => _getInstance();// 示例一下其他写法
static SingletenSimple _instance; SingletenSimple._(); static SingletenSimple _getInstance(){ if(_instance == null){ _instance = SingletenSimple._(); } return _instance; } }
复制代码


继承,协议,Mixin

Swift 支持单继承类,也支持继承协议(Swift 面向协议编程(笑~)),Dart 支持 Mixin 混合模式,实际是和 Swift 一样的效果,继承是多继承,但是 superclass  只有一个。另外截止到当前版本,Dart 没有协议支持,协议实现需要用 abstract 抽象类代替。Dart 的官网对 Mixin 定义

每个对象都是类的实例,都派生自 Object 。基于 Mixin 的继承实现,虽然每个类(除了 Object)都只有一个父类(superclass),但是类的内部可以被其他类复用。


思考一下, Mixin 是不是对多重继承的改进?子继承父的概念没有变,但是又扩大了继承的复用面。从这方面看 Swift 其实也实现了 Mixin ,因为 Swift 支持协议扩展,协议又可以被继承。


  • Swift

protocol Runable{    func run();}
extension Runable{ func run(){ print("I am running...") }}
class Human{ var legs:String? var hands:String?
}
class Person:Human,Runable{ //继承实现 let name:String let age:Int let height:Double init(name: String, age: Int, height: Double) { self.name = name self.age = age self.height = height } }
let p = Person(name: "summber", age: 6, height: 1.12);p.run() // I am running...
复制代码


  • Dart

abstract class Runable {  void run(){    print('I am running...');  }}
abstract class Human { String legs; String hands;}
class Person extends Human with Runable { //extends 是继承,with 是 mixin 的写法 String name; int age; double height;
Person(this.name,this.age,this.height);}
var p = Person("summber",6,1.12);p.run(); // I am running...
复制代码

更多阅读请查看 Swift 类与结构体,Dart 类的结构

属性

Dart 中称为 instance variables , Swift 中称为 property 。前面例子里有很多的声明了,这里主要关注一下 get set 的方法和 Swift 中特殊的 Property observers 。


  • Swift

class Circle {    init(radius:Double){        self.radius = radius    }    var radius:Double    var diameter:Double{ // 如果只有get 方法可以只写 return radius * 2.0        get {            return radius * 2.0        }        set {            radius = newValue / 2.0        }    }    }
复制代码


  • Dart

class Circle{    double radius;
Circle(this.radius);
double get diameter{ return radius * 2.0; } set diameter(double newvalue){ radius = newvalue / 2.0; }}
复制代码

Swift 支持属性值观察,类似 KVO 


  • Swift

    var diameter:Double{                willSet(newDiameter){           print("will set old: \(diameter), new: \(newDiameter)")        }        didSet {            print("did set old:\(oldValue), new: \(diameter)")        }    }
复制代码

阅读更多 Swift 属性篇 Dart 的 instance variablesgetter & setter

扩展

Swift 支持对类,现有基本类型,结构体,枚举,协议进行扩展。Swift 可以写出如下的代码,更多的内容查看 Swift 扩展。自 Dart 2.7 开始也支持扩展


  • Swift

extension Int {    func addOne() -> Self {       return self + 1    }}
print(3.addOne()) // 4
复制代码


  • Dart

extension numparer on int {  int addone(){    return this + 1;  }}var num = 3;print(3.addone()); // 4
复制代码


枚举

Swift 的枚举非常强大,除了面向协议的编程还诞生了面向枚举的编程(这不是真的,别信。。。)。Dart 就只有基本支持。由于 Swift 的枚举表达能力过强,我们这里只讨论基本的样式,更多的请查看 Swift 的枚举


  • Swift

//普通版本enum Human{    case man    case wowan}
class Person{ var name:String var age:Int init(name:String,age:Int) { self.name = name self.age = age }}
class Girl:Person{ var bag:String?}
//常见版本enum Human{ case man(he:Person) case woman(she:Girl) func turn() -> Self { switch self { case .man(let he): print("he \(he.name) will become she") return .woman(she: Girl(name: he.name, age: he.age)) case .woman(let girl): print("she \(girl) will become he") return .man(he: Person(name: girl.name, age: girl.age)) } }}
let aMan = Human.man(he: Person(name: "Tom", age: 20))aMan.turn() //he Tom will become she
复制代码


  • Dart

enum Person {	man,  woman,}
复制代码


结构体

Swift 支持类也支持结构体,两者最大的不同是结构体是值类型,类是引用类型(如果不知道值类型和引用类型,面壁思过去。。。)。


struct Person {    var name:String    let age:Int    let height:Double}
let p = Person(name: "Summer", age: 6, height: 1.12)var q = pq.name = "Spring"
print(p.name)// Summer
复制代码

阅读更多 Swift Copy-on-Write, 类与结构体


错误处理

Swift 和 Dart 都采用了 try/catch 的方式处理错误。两者有少量的不同,Swift 会更严格一些,要产生异常的代码方法名以 throws 结尾,异常的类型必须继承 Error , Dart 可以 throw 任何类型。


  • Swift

enum AccountError: Error {  case insufficientFunds}
class BankAccount { var balance: Double init(balance: Double) { self.balance = balance } func withdraw(amount: Double) throws { //相对Dart 增加 throws 关键字 if amount > balance { throw AccountError.insufficientFunds } balance -= amount }}
var account = BankAccount(balance: 100)do { try account.withdraw(amount: 50) // ok try account.withdraw(amount: 200) // throws} catch AccountError.insufficientFunds { print("Insufficient Funds")}
复制代码

Swift 有三种 try try? try! 的使用方式, try? 可以不用 try/catch ,而 try! 适合在完全确定不会有异常的情况下使用,但是不建议在生产环境使用,如果出现异常会发生 Crash,可以写测试案例的使用。

var account = BankAccount(balance: 100)try? account.withdraw(amount: 50) // oktry? account.withdraw(amount: 200) // 不会产生异常
var account = BankAccount(balance: 100)try! account.withdraw(amount: 50) // oktry! account.withdraw(amount: 200) // crash
复制代码


  • Dart

class BankAccount{  double balance;  BankAccount(this.balance);
void withdraw(double account){ if(account > balance){ throw(Exception('Insufficient funds')); } balance -= account; }}

var account = BankAccount(balance: 100);try { account.withdraw(50); // ok account.withdraw(200); // throws} catch (e) { print(e); // prints 'Exception: Insufficient funds'}
复制代码

总体来看 Swift 语言设计的更加健壮一些,可以清晰的看到调用的方法是否会有异常。查看更多的内容 Swift 如何处理错误, Dart 异常处理

泛型

两者都支持泛型, Swift 对泛型的支持更好一些,支持泛型约束和 Associated Type


  • Swift

struct Stack<Element> {  var items = [Element]()  mutating func push(_ item: Element) {    items.append(item)  }  mutating func pop() -> Element {    return items.removeLast()  }}
复制代码


  • Dart

class Stack<Element> {  var items = <Element>[]  void push(Element item) {    items.add(item)  }  void pop() -> Element {    return items.removeLast()  }}
复制代码

阅读更多 Swift 泛型, Dart 的泛型

访问控制

访问控制对于封装代码非常有帮助。Swift 有 5 级控制,分别是 open public  internal file-private private 。Dart 则只有 public 和 private 两种,使用 _ 区分。


  • Swift

- open 和 public 都可以被模块以外的代码调用,不同的是 open 修饰的 class 和成员变量可以被继承和修改,而 public 不可以。因此是定义模块与外界接口使用的关键词。

- internal 是默认的控制等级,可以不用写。可以在模块以内访问,但不可以在模块意外访问

- file_private 修饰的方法和属性只能在当前的定义的文件内访问

- private 修饰的方法和属性只能在当前类中访问


public class SomePublicClass {}internal class SomeInternalClass {}fileprivate class SomeFilePrivateClass {}private class SomePrivateClass {}
public var somePublicVariable = 0internal let someInternalConstant = 0fileprivate func someFilePrivateFunction() {}private func somePrivateFunction() {}
复制代码


  • Dart

class HomePage extends StatefulWidget { // public  @override  _HomePageState createState() => _HomePageState();}
class _HomePageState extends State<HomePage> { ... } // private,其他文件也无法访问
复制代码

阅读更多 Swift 的 访问控制,Dart 的库与可见性

异步编程

终于到了 Swift 不擅长的,而 Dart 擅长的一章了。 来自谷歌 Go 语言浓浓的影子 - microTask , Dart 运行时可以开一篇文章单独介绍了,有兴趣的可以先阅读掘金上一篇高质量的文章,官网的资料也不错在此。前两篇文章都没有讲明白异步编程到底是什么,异步编程的的神秘面纱其实在 ES6 中有可以参考的实现,可以阅读阮一峰老师的文章,答案应该是 协程 。Swift 已经有提案了,应该也会在最近几个版本实现。

下面 Dart 的代码实现了从服务端获取 token ,然后保存到本地,在通过 token 获取用户信息。

Future<UserProfile> getUserProfile(UserCredentials credentials) async {  final accessToken = await networkService.signIn(credentials);  await secureStorage.storeToken(accessToken, forUserCredentials: credentials);  return await networkService.getProfile(accessToken);}
复制代码

Swift 版本的实现

func getUserProfile(credentials: UserCredentials, completion: (_ result: UserProfile) -> Void) {  networkService.signIn(credentials) { accessToken in    secureStorage.storeToken(accessToken) {      networkService.getProfile(accessToken, completion: completion)    }  }}
复制代码

可以看到 Dart 的可读性简直秒杀 Swift 的回调地狱。在 Swift 支持异步编程之前可以用 Promises 减轻对开发者的伤害。

Dart 的 Steams 是对异步事件的支持,在 flutter 的状态管理上有出色的应用。Steams 也会单独写一篇文章进行探索,关于 Steams 的内容非常多,谷歌的程序员还提出了 BLoC 模式。


内存管理

Swift 沿用了 iOS 一贯的传统 ARC(自动引用计数),Dart 采用的是垃圾回收。对于 Swift 要小心循环引用,更加详细的内容查看这里

编译和执行

首先科普一下 AOT(ahead-of-time)和 JIT (just-in-time)


  • JIT


在应用启动的时候编译,运行时不断优化生成代码,启动速度会慢一些。适用于动态语言,比如 JavaScript 就是 JIT 方式运行,Dart 采用 JIT 运行时速度与 JavaScript 差不多。通常需要运行时环境。


  • AOT


在编译程序的时候就执行完毕,直接将程序编译为机器语言,启动速度快。一般用于静态语言。如 C++, Swift。

Dart 支持了 JIT 和 AOT 两种方式的编译。

flutter 在开发的时候使用 JIT 方式,在发布的时候使用 AOT。Dart 为我们提供了两种方式,在开发环节通过 JIT 可以动态更新代码不需要每次重新编译程序,快速调试 UI,发布的时候使用 AOT 方式运行不会有性能损失。


并发支持

Swift 使用了 GCD 多线程的方式进行支持,线程间通信需要注意锁。Dart 采用的是 isolates 消息通信,不需要考虑锁(并不是完全不需要考虑,对于同一 IO 的访问还是需要)。

最后

举个不太恰当的例子, Swift 这门语言就像个精密机械库,细致,精巧,严谨,安全。Dart 却像个赛克朋克机器人,粗莽,充满力量。Dart 发自 2011 年,年长 Swift 3 年,不过由于其独到的跨平台技术,开发时 JIT 技术,异步编程,也会在以后的移动终端占领一席之地, 在 Webassembly 来到后 Web 时代时,说不准可以统一全部终端。

主要借鉴的文章,本文地址链接,请轻爬。


发布于: 2020 年 06 月 28 日阅读数: 195
用户头像

柠檬水

关注

还未添加个人签名 2018.05.16 加入

还未添加个人简介

评论

发布
暂无评论
Dart vs Swift