讀古今文學網 > iOS編程基礎:Swift、Xcode和Cocoa入門指南 > 1.12 為何使用實例 >

1.12 為何使用實例

雖然沒有實例這個事物,不過一個對像類型本身就是個對象。之所以這麼說是因為我們可以向對像類型發送消息:可以將對像類型看作命名空間,並顯式進入該命名空間中(如Manny.Klass)。此外,既然存在類成員與靜態成員,我們可以直接調用類、結構體或枚舉類型的方法,還可以引用類、結構體或枚舉類型的屬性。既然如此,實例還有存在的必要嗎?

答案與實例屬性的本質有關。實例屬性的值的定義與特定的實例有關,這正是實例真正的用武之地。

再來看看Dog類。我為它添加了一個name屬性和一個bark方法;請記住,它們分別是實例屬性與實例方法:


class Dog {
    var name = ""
    func bark {
        print("woof")
    }
}  

Dog實例剛創建出來時name是空的(一個空字符串)。不過其name屬性是個var,因此一旦創建了Dog實例,我們就可以為其name賦予一個新的String值:


let dog1 = Dog
dog1.name = "Fido"  

還可以獲取Dog實例的name:


let dog1 = Dog
dog1.name = "Fido"
print(dog1.name) // "Fido"  

重要之處在於我們可以創建多個Dog實例,兩個不同的Dog實例可以擁有不同的name屬性值(如圖1-2所示):


let dog1 = Dog
dog1.name = "Fido"
let dog2 = Dog
dog2.name = "Rover"
print(dog1.name) // "Fido"
print(dog2.name) // "Rover"  

注意,Dog實例的name屬性與Dog實例所賦予的變量名之間沒有任何關係。該變量只不過是一個盒子而已。你可以將一個實例從一個盒子傳遞給另一個。不過實例本身會維護自己的內在統一性:


let dog1 = Dog
dog1.name = "Fido"
var dog2 = Dog
dog2.name = "Rover"
print(dog1.name) // "Fido"
print(dog2.name) // "Rover"
dog2 = dog1
print(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"。

簡而言之,實例既包含代碼又包含數據。代碼來自於實例的類型,並且與該類型的所有其他實例共享,不過數據只屬於該實例本身。只要實例存在,其數據就一直存在。在任意時刻,實例都有一個狀態——自身屬性值的完整集合。實例是維護狀態的一個設備,它是數據的一個存儲箱。