第一版是為 Lua 5.0 編寫的。雖然在很大程度上仍然適用於後續版本,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您還可以協助 支援 Lua 專案


17 – 弱表格

Lua 具有自動記憶體管理功能。程式只會建立物件(表格、函式等);沒有用於刪除物件的函式。Lua 會使用垃圾回收自動刪除成為垃圾的物件。這讓您免於承擔記憶體管理的大部分負擔,更重要的是,讓您免於承擔與該活動相關的大部分錯誤,例如懸浮指標和記憶體外洩。

與其他一些收集器不同,Lua 的垃圾收集器不會出現循環問題。使用循環資料結構時,您不需要採取任何特殊動作;它們會像其他資料一樣被收集。儘管如此,有時即使是更智慧的收集器也需要您的協助。沒有垃圾收集器可以讓您忘記所有關於記憶體管理的煩惱。

垃圾收集器只能收集它確定為垃圾的內容;它無法知道您認為是什麼垃圾。一個典型的範例是堆疊,它使用陣列和指標實作到頂端。您知道陣列的有效部分只到頂端,但 Lua 不知道。如果您透過遞減頂端來彈出元素,陣列中留下的物件對 Lua 來說並非垃圾。同樣地,儲存在全域變數中的任何物件對 Lua 來說都不是垃圾,即使您的程式永遠不會再使用它。在這兩種情況下,都由您(即您的程式)將 nil 指定給這些位置,以便它們不會鎖定其他已釋放的物件。

但是,僅清理您的參考並不總是足夠的。有些結構需要您和收集器之間額外的協作。一個典型的範例發生在您想要在程式中保留某種類型所有現存物件(例如檔案)的集合時。這似乎是一項簡單的任務:您所要做的就是將每個新物件插入集合中。但是,一旦物件進入集合,它就永遠不會被收集!即使沒有其他人指向它,集合也會指向它。Lua 無法知道這個參考不應阻止回收物件,除非您告訴 Lua。

弱表格是您用來告訴 Lua 參考不應阻止回收物件的機制。弱參考是垃圾收集器不考慮的物件參考。如果指向物件的所有參考都很弱,則會收集物件並以某種方式刪除這些弱參考。Lua 將弱參考實作為弱表格:弱表格是一個其中所有參考都很弱的表格。這表示,如果物件只保存在弱表格中,Lua 最終會收集物件。

表格具有鍵和值,且兩者都可能包含任何類型的物件。在正常情況下,垃圾回收器不會回收顯示為可存取表格的鍵或值的物件。亦即,鍵和值都是參照,因為它們會防止回收它們所參照的物件。在弱表格中,鍵和值可能是弱的。這表示有 3 種類型的弱表格:具有弱鍵的表格、具有弱值的表格,以及完全弱的表格(其中鍵和值都是弱的)。不論表格類型為何,當鍵或值被回收時,整個項目會從表格中消失。

表格的弱點由其元表的 __mode 欄位提供。此欄位的數值(如果存在)應為字串:如果字串包含小寫字母 `k´,表格中的鍵就是弱的;如果字串包含小寫字母 `v´,表格中的值就是弱的。以下範例雖然是人為的,但說明了弱表格的基本行為

    a = {}
    b = {}
    setmetatable(a, b)
    b.__mode = "k"         -- now `a' has weak keys
    key = {}               -- creates first key
    a[key] = 1
    key = {}               -- creates second key
    a[key] = 2
    collectgarbage()       -- forces a garbage collection cycle
    for k, v in pairs(a) do print(v) end
      --> 2
在此範例中,第二次指定 key = {} 會覆寫第一個鍵。當回收器執行時,沒有其他參照指向第一個鍵,因此它會被回收,而表格中對應的項目也會被移除。不過,第二個鍵仍錨定在變數 key 中,因此不會被回收。

請注意,只有物件可以從弱表格中回收。數值和布林值等值無法回收。例如,如果我們在表格 a(來自我們之前的範例)中插入一個數字鍵,它永遠不會被回收器移除。當然,如果對應於數字鍵的值被回收,則整個項目會從弱表格中移除。

字串在此處呈現出細微差異:雖然字串可以回收,但從實作觀點來看,它們不像其他可回收物件。其他物件(例如表格和函式)會明確建立。例如,每當 Lua 評估 {} 時,它會建立一個新表格。每當它評估 function () ... end 時,它會建立一個新函式(實際上是一個閉包)。不過,當 Lua 評估 "a".."b" 時,它會建立一個新字串嗎?如果系統中已經有一個字串 "ab" 呢?Lua 會建立一個新字串嗎?編譯器可以在執行程式之前建立那個字串嗎?這並不重要:這些都是實作細節。因此,從程式設計師的觀點來看,字串是值,而不是物件。因此,就像數字或布林值一樣,字串不會從弱表格中移除(除非其關聯值被回收)。