虽然没有实例这个事物,不过一个对象类型本身就是个对象。之所以这么说是因为我们可以向对象类型发送消息:可以将对象类型看作命名空间,并显式进入该命名空间中(如Manny.Klass)。此外,既然存在类成员与静态成员,我们可以直接调用类、结构体或枚举类型的方法,还可以引用类、结构体或枚举类型的属性。既然如此,实例还有存在的必要吗?
答案与实例属性的本质有关。实例属性的值的定义与特定的实例有关,这正是实例真正的用武之地。
再来看看Dog类。我为它添加了一个name属性和一个bark方法;请记住,它们分别是实例属性与实例方法:
class Dog { var name = "" func bark { print("woof") }}
Dog实例刚创建出来时name是空的(一个空字符串)。不过其name属性是个var,因此一旦创建了Dog实例,我们就可以为其name赋予一个新的String值:
let dog1 = Dogdog1.name = "Fido"
还可以获取Dog实例的name:
let dog1 = Dogdog1.name = "Fido"print(dog1.name) // "Fido"
重要之处在于我们可以创建多个Dog实例,两个不同的Dog实例可以拥有不同的name属性值(如图1-2所示):
let dog1 = Dogdog1.name = "Fido"let dog2 = Dogdog2.name = "Rover"print(dog1.name) // "Fido"print(dog2.name) // "Rover"
注意,Dog实例的name属性与Dog实例所赋予的变量名之间没有任何关系。该变量只不过是一个盒子而已。你可以将一个实例从一个盒子传递给另一个。不过实例本身会维护自己的内在统一性:
let dog1 = Dogdog1.name = "Fido"var dog2 = Dogdog2.name = "Rover"print(dog1.name) // "Fido"print(dog2.name) // "Rover"dog2 = dog1print(dog2.name) // "Fido"
上述代码并不会改变Rover的name;它改变的是dog2盒子中的狗,将Rover替换成了Fido。
基于对象编程的威力现在开始显现出来了。有一个Dog对象类型,它定义了什么是Dog。我们对Dog的声明表示任何一个Dog实例都有一个name属性和一个bark方法。不过每个Dog实例都有自己的name属性值。它们是不同的实例,分别维护着自己的内部状态。因此,相同对象类型的多个实例的行为都是类似的:Fido与Rover都可以吼叫,如果向它们发送bark消息它们就会这么做,但它们是不同的实例,可以有不同的属性值:Fido的name是"Fido",而Rover的name是"Rover"。
图1-2:具有不同属性值的两只狗
(对于数字1与2来说同样如此,不过事实却并不是那么显而易见。Int是一个value属性。1是一个Int,其值是1,2表示值为2的Int。不过,这一事实在实际开发中却没那么有趣,因为你显然不会改变1的值!)
实例是其类型的实例方法的反映,但这并非全部;它还是实例属性的集合。对象类型负责实例所拥有的属性,但对这些属性的值并不是必需的。当程序运行时,值可以发生变化,并且只会应用到特定的实例上。实例是特定属性值的集合。
实例不仅要负责值,还要负责属性的生命周期。假设我们创建了一个Dog实例,并为其name属性赋予了值"Fido"。那么只要我们没有使用其他值替换掉其name值并且该实例处在存活状态,那么该Dog实例就会一直持有字符串"Fido"。
简而言之,实例既包含代码又包含数据。代码来自于实例的类型,并且与该类型的所有其他实例共享,不过数据只属于该实例本身。只要实例存在,其数据就一直存在。在任意时刻,实例都有一个状态——自身属性值的完整集合。实例是维护状态的一个设备,它是数据的一个存储箱。