使用值类型构建更好的Swift应用程序

jopen 6年前

 

在WWDC2015上,苹果工程师Doug Gregor和Bill Dudney 评价 了Swift对值类型的支持,并解释了如何通过它实现一种灵活的不可变性,以构建更好的应用。

Gregor首先评价了Objective-C中常用的引用语义。引用语义的主要问题在于它有可能产生预期之外的对象共享行为,这种行为有可能导致 对象的属性 被暗中改动。Objective-C程序员对此已经十分了解了,因为许多Cocoa和Cocoa Touch类,像NSString、NSURLRequest,以及所有的集合调用时都需要拷贝。所以为了让程序员更易于进行防御性的拷贝,Objective-C语言甚至为属性提供了一个copy特性,当给一个给定的属性分配对象时,运行时本身就会拷贝一个副本。

对于性能和内存的使用来说,防御性拷贝明显不是最优的措施,尤其在使用不当时更可能造成微妙的问题。

随后,Gregor继续探索不可变性是否是这个问题的正确答案。许多Cocoa类,像NSDate、NSURL、UIImage、 NSNumber和其它许多类都强制实施不可变性。根据Gregor的评价,不可变性拥有许多优点,比如没有副作用和可共享,但它也会生成尴尬的接口,并且不能有效地映射到机器模型。为了具体说明这一点,Gregor在Haskell和Swift中分别运行Eratosthenes筛法来举例说明不可变性是如何牺牲性能的。

根据Gregor的评价,对值语义的使用正是这类问题的解决办法,而Swift完全支持这种值语义:

  • 所有的基本类型,比如Int、Double、String等都是值类型;
  • Swift中的所有集合类型,比如Array、Set和Dictionary都是值类型;
  • 值类型可以进行组合,比如tuples、structs和enums仅包含值类型,因而它们自身也是值类型,从而可以根据值语义建立抽象类型。

值类型仅仅就是值,并且它们是不可变的。它们没有标识符,所以只能以值来区分。这就需要所有的值类型都遵守Equatable协议。

protocol Equatable {      /// Reflexive - `x == x` is `true`      /// Symmetric - `x == y` then `y == x`      /// Transitive - `x == y` and `y == z` then `x == z`     func ==(lhs:Self, rhs:Self) -> Bool  }

此外,值类型允许应用程序根据需要在可变性和不可变性之间取得适当的平衡。Gregor表示:实际上,你可以对值类型使用let关键字,以指定一个不会改变的变量,或者使用var来指定一个可以在不影响其它值的前提下更新自身的值的变量。
let numbers = [1, 2, 3, 4]  var strings = [String]()  for x in numbers {      strings.append(String(x))  }

如前文所述,值类型是不可变的,所以它们只能被拷贝。但是,Gregor说,它们的拷贝操作开销很低,对于简单的值类型,比如Int、 Double、CGPoint等,它们的拷贝时间是个常数。对于可扩展的数据结构,将会使用写时拷贝(copy-on-write)技术,只有当值改变时,才会建立一个副本,所以这种技术比默认拷贝方式更有效率。

此次演讲的第二部分专注于提供一个使用Swift值类型进行编程的动手实验示例。

查看英文原文: Building Better Swift Apps Using Value Types