类(二)

2018-05-23  本文已影响62人  小橘子成长记

了解状态和副作用

类的引用和可变特性导致了大量的编程问题。如果用一个新值更新类的实例,那么对该实例的每个引用也将看到新值。

你可以利用这个优势。把一个学生的实例传递给一个运动队,一个成绩单和一个班级花名册。所有这些实体都需要知道学生的成绩,由于它们都指向同一个实例,所以它们都将看到新的分数,跟学生实例中的一样。


QQ20180523-100938@2x.png

共享的结果是类实例拥有状态(比如学生的成绩就是一个状态,某一时刻某个实例的状态就是成绩等于3)。状态的改变可以有时是显而易见的,但有时候不是。

为了说明这一点,在Student类上增加一个credits:

var credits = 0.0

更新 recordGrade(_:) 方法去使用新的属性:

func recordGrade(_ grade: Grade) {
  grades.append(grade)
  credits += grade.credits
}

在这个稍微修改过的Student的例子中,recordGrade(:)现在将credits(每科学分)的数量添加到credits(学生学分)属性中。调用recordGrade(:)具有更新credits的副作用。

现在,观察一下副作用是如何导致不明显的行为的:

jane.credits // 7
// The teacher made a mistake; math has 5 credits
math = Grade(letter: "A", points: 20.0, credits: 5.0)
jane.recordGrade(math)
jane.credits // 12, not 8!

无论谁修改Student类,大家都认为相同学科的分数不会被记录两次。其实被记录了两次,一开始Math是4分,后来的Math是5分,增加了1分,正确的应该由原来的7分变成8分。但是,现在是7分直接加了Math的5分。

虽然在一个小的例子中混淆了,当类的大小和复杂性增加时,可变性和状态可能会非常不和谐。学生的grade是20个存储属性,有10个方法,这样的情况会更常见。

使用extension扩展一个类

正如你看到的结构,可以使用extension关键字重新打开类,以添加方法和计算属性。向Student添加一个完整的计算属性:

extension Student {
  var fullName: String {
    return "\(firstName) \(lastName)"
  }
}

还可以使用继承将功能添加到类中。你至可以添加新的存储属性来继承类。在下一章中,将详细探讨这一技术。

何时使用类和结构

既然你已经了解了类和结构之间的区别和相似之处,你可能会想“我怎么知道该使用哪一个呢?”

值和对象

虽然没有严格的规则,但是有一种方法,可以思考一下值和引用语义,使用结构作为值,使用类作为具有标识的对象。对象是引用类型的实例。这样的实例标识。这意味着每个对象都是唯一的,没有两个对象因为它们持有相同的状态被认为是相等的。这就是为什么要使用===来查看对象是否真正相等,而不只是包含相同的状态。这与值类型的实例相反,值类型是值,根据定义,如果它们是相同的值那么它们就是相等的。

例如:配送范围是一个值,应该作为一个struct来实现,而student是一个对象,应该作为一个类来实现。没有两个学生因为他们有相同的名字就被认为是相等的!

速度

速度方面也需要考虑,因为结构依赖于更快的堆栈,而类依赖于较慢的堆。如果你创建一个类型的多个实例(数百到数千个),或者如果这些实例只在内存中存在很短的时间,那么你通常应该倾向于使用struct。如果你的实例在内存中具有更长的生命周期,或者你创建相对较少的实例,那么在堆上创建类实例通常不会产生过多的开销。

例如,你可能希望使用一个struct来计算运行路线的总距离,使用许多基于gps的路标,比如“结构”中使用的位置结构。你不仅会创建许多路径点,而且还会在修改路线时快速创建和销毁它们。

相反,您可以使用类作为对象来存储路径历史,因为每个用户只有一个对象,并且你可能会在用户中使用相同的路径对象。

极简主义的方法

另一种方法是只使用你所需要的。如果你的数据永远不变,或者你需要一个简单的数据存储,那么就使用结构。如果你需要更新你的数据,你需要它包含逻辑来更新它自己的状态,那么使用类。通常,最好从结构开始。如果稍后你需要一个类的附加功能,那么将struct转换为一个类。

结构与类重点

结构
•用于表示值。
•隐式复制值。
•当用let声明时,变得完全不可变。
•快速内存分配(堆栈)


•用于表示具有标识的对象。
•隐式共享对象。
•内部可以保持可变,即使使用let声明。
•内存分配较慢(堆)

关键点

•与结构类似,类是一个命名类型,它可以具有属性和方法。
•类在使用时共享引用。
•类实例称为对象。
•对象是可变的。
•可变性引入状态,在管理对象时增加了另一层次的复杂性。
•当需要引用语义时使用类,当需要值语义时使用结构。

上一篇下一篇

猜你喜欢

热点阅读