12.1 存储属性

存储属性可以存储数据,分为常量属性(用关键字let定义)和变量属性(用关键字var定义)。存储属性适用于类和结构体两种Swift面向对象类型。

12.1.1 存储属性概念

我们在前面的章节中曾用到过属性,例如11.4节的员工类(Employee)和部门类(Department)。它们的类图如图12-1所示,Employee 的部门属性deptDepartment之间进行了关联。

{%}

图 12-1 类图

我们可以在定义存储属性时指定默认值,示例代码如下:

  1. class Employee {
  2. let no : Int = 0
  3. var name : String = ""
  4. var job : String?
  5. var salary : Double = 0
  6. var dept : Department?
  7. }
  8. struct Department {
  9. let no : Int = 0
  10. var name : String = ""
  11. }
  12. var emp = Employee()
  13. emp.no = 100 //编译错误 ①
  14. let dept = Department()
  15. dept.name = 30 //编译错误 ②
  16. let emp1 = Employee()
  17. emp1.name = "Tony"

实例通过点(.)运算符调用属性,代码第①行试图修改常量属性,程序会发生编译错误。第②行代码也会发生编译错误,因为实例dept本身是常量,即便它的属性name是变量属性,也不能修改。但是代码第③行emp1.name = "Tony"却可以编译通过,emp1实例也是常量,name是变量属性。这是因为emp1是类实例,是引用类型,dept是结构体实例,是值类型。引用类型相当于指针,其常量也可以修改,但值类型的常量是不能修改的。

12.1.2 延迟存储属性

由于EmployeeDepartment有关联关系,Employee类中的dept属性关联到了Department结构体。这种关联关系体现为:一个员工必然隶属于一个部门,而一个部门有很多员工。一个员工实例对应于一个部门实例。

下面看以下代码实现:

  1. class Employee {
  2. var no : Int = 0
  3. var name : String = ""
  4. var job : String?
  5. var salary : Double = 0
  6. var dept : Department = Department()
  7. }
  8. struct Department {
  9. let no : Int = 0
  10. var name : String = ""
  11. }
  12. let emp = Employee()

在代码第②行创建Employee实例的时候,也会同时在第①行实例化dept(部门)属性。然而程序或许不关心他隶属于哪个部门,只关心他的no(编号)和name(姓名)。虽然不使用dept实例,但是仍然会占用内存。在Java中,有一种数据持久化技术叫Hibernate1。为了应对这种情况,Hibernate有一种延时加载技术,Swift也采用了延迟加载技术。修改代码如下:

1Hibernate是一种Java语言下的对象关系映射解决方案。它是使用GNU宽通用公共许可证发行的自由、开源的软件。它为面向对象的领域模型到传统的关系型数据库的映射,提供了一个使用方便的框架。——引自于维基百科 http://zh.wikipedia.org/wiki/Hibernate

  1. class Employee {
  2. var no : Int = 0
  3. var name : String = ""
  4. var job : String?
  5. var salary : Double = 0
  6. lazy var dept : Department = Department()
  7. }
  8. struct Department {
  9. let no : Int = 0
  10. var name : String = ""
  11. }
  12. let emp = Employee()

我们在dept属性前面添加了关键字lazy声明,这样dept属性就是延时加载。延时加载,顾名思义,就是dept属性只有在第一次访问它的时候才加载,如果永远不访问,它就不会创建,这样就可以减少内存占用。

12.1.3 属性观察者

为了监听属性的变化,可以使用Swift提供的以下属性观察者。

  • willSet在设置新的值之前调用。

  • didSet在新值被设置之后马上调用。

我们可以根据需要,使用部分或全部的属性观察者。它们不能应用于延迟存储属性,但能够应用于一般的存储属性和计算属性。计算属性所需的属性观察者将在第15章介绍。