聲明一個變量
我們使用 var 來聲明一個變量,就好像從櫃子裡拿出了一個試管放在了實驗台上;
我們給變量規定了一個類型,就好像在試管上貼上了標籤;
那麼放入的試劑就必須是標籤上標記了的——否則可能導致中毒或者爆炸。
同樣的,如果我們試圖給一個儲存器放入一個錯誤的數據類型,那麼編譯器就會報錯——沒錯總有辦法能夠騙過編譯器——反正我不會教你這個方法,那樣就會導致程序崩潰啦。
所以說,試管有容量,我們聲明的類型也有大小限制,超限也會報錯——一般夠用的。
這就是為什麼會有個術語叫做“溢出“。
如果你對化學不感興趣
那好,我們換一個栗子:
我們都知道有一些動物是吃草的,而有一些動物則只吃這些吃草的動物?
那麼我這裡有狼和羊,狼要吃羊,而羊要吃草。
你給狼吃草或者給羊吃狼都是不對的………………
好了,我們還是下一個話題吧。
對像不是值
現在,我們知道了儲存器是什麼,我們再來說說“放”到儲存器裡的另外一種東西——對象。
在接觸了面向對象編程之後,我們就必鬚麵對一個新的概念——什麼是對象,當然,這個問題我們在上一節課已經討論過了,這節課我們重點來討論如何把大象放進冰箱的問題。
當我們這樣聲明:
1 2 3 4 |
class MyClass {} let obj1 = MyClass() let obj2 = MyClass() |
難道不是把實例放進了儲存器裡嗎? ——還真不是。
實際上還真的沒有能儲存對象的儲存器,就像我們說的,也沒有能放進大象的家用冰箱。
悄悄告訴你,對像其實放在“堆”上……沒錯,就是垃圾堆。
好了,這裡我們要請出除了 nil 這個特殊的值以外的另一個特殊的值了:
引用
其實引用就是指針。只不過它把指針給包裝了——具體包裝成了什麼我們不清楚——恐怕只有蘋果的攻城獅能告訴你了。總之,我們把這個值叫做引用。
不同於 C 語言的指針,雖然引用也是一個指向堆裡邊某個對象的指針,但它的的確確是不能被直接編輯的。
電視機和遙控器
我談起這兩個東西你可能又覺得古老……正是因為它古老,所以我覺得可能所有人都應該是接觸過的。
當你生成了一個對象,那我們就說你買了一個電視機;那麼對應放到你手裡的,自然就是遙控器啦。所以我們說,不論你的生成的對象實例是多麼複雜,多麼龐大,它的遙控器永遠是那個樣子——頂多多了幾個按鈕(方法)而已,真正的實例其實是放在堆上的。
說到堆
我們再來說一說儲存器裡的引用與堆上對象實例的對應關係。
一般我們聲明一個儲存器,然後賦值給它一個對象,那麼這個儲存器就存了一個引用,然後對象的實例被扔在了堆上。那麼,這時候我們的遙控器和電視機是一對一對應的。
就像這樣,我們從同一個類生成了兩個實例,兩個實例被放在了堆上,而我們手中的變量則存了它們的引用——就像是門牌號。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class A { var num = 0 func doSomething() { print(num) } } var objA = A() objA.num = 9999 objA.doSomething() //9999 var objB = A() objB.num = 2222 objB.doSomething() //2222 |
那如果我要把引用複制一份給別人呢?
1 2 3 4 5 6 |
var objC = objA objC.num = 8888 objA.doSomething() //8888 objC.doSomething() //8888 objB.doSomething() //2222 |
沒錯!那麼另外一個變量也就指向了堆上相同的對象!
現在其實 objA 和 objC 是同一個對象的引用呢!
現在我這麼做:
1 2 3 4 5 6 7 8 |
var objC = objA objC.num = 8888 objB = objC objB.num = 4444 objA.doSomething() //4444 objC.doSomething() //4444 objB.doSomething() //4444 |
哈!都成了同一個對象! ……等等,那 objB 中原來的對象哪裡去了?這樣豈不是再也沒辦法訪問到它了嗎? ?
沒錯,這個時候 objB 所引用的原來的那個對象就孤零零的存在於堆上——沒關係,它早就被 GC(垃圾收集器)給帶走了(別問我帶到了哪裡)。
做個栗子吧!
其實,就是上節課的課後題啦~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import Foundation class Echo { var count = 0 func hello() { print("hellooooo.....") } } var e1 = Echo() var e2 = e1 //把 e1 的引用赋值给 e2 //var e2 = Echo() var x = 0 while x < 4 { e1.hello() e1.count = e1.count + 1 if x == 3 { e2.count = e2.count + 1 } if x > 0 { e2.count = e2.count + e1.count } x = x + 1 } print(e2.count) |
運行結果是24而不再是10.
課後題
我們聲明一個叫做 HeapQuiz 的類,這個類只包含一個屬性就是 ID,我們用它來區分實例,現在我們生成了5個實例,然後把它們的引用打亂,現在,你能說出這五個引用對應的實例的 ID 嗎?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class HeapQuiz { var id = 0 } var x = 0 var hq:[HeapQuiz?] = Array<HeapQuiz?>(count: 5, repeatedValue: HeapQuiz()) while x < 3 { hq[x]?.id = x x++ } hq[3] = hq[1] hq[4] = hq[1] hq[3] = nil hq[4] = hq[0] hq[0] = hq[3] hq[3] = hq[2] hq[2] = hq[0] |
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/1109.html
註釋