弱表指南 |
|
在使用垃圾回收的電腦語言,例如 Lua,如果對物件的參照不會防止回收該物件,則此參照稱為弱參照。弱參照可協助在物件已回收時確定,以及在不阻礙物件回收的情況下快取物件。
Lua 並未提供個別弱參照的介面,而是提供稱為弱表的高階建構函數。在弱表中,鍵值和/或變數為弱參照。如果此類表中的鍵值或變數已回收,則會移除該表中的該項目。
以下為弱表作業的一個完整,但非常牽強的範例
t = {} setmetatable(t, { __mode = 'v' }) do local someval = {} t['foo'] = someval end collectgarbage() for k, v in pairs(t) do print(k, v) end
嘗試使用已註解或未註解之 collectgarbage()
呼叫此範例。使用呼叫時,程式不會印出任何東西,因為會回收個別表項目的值。
建立弱表的方式是設定元資料為具備 __mode
屬性的元資料。在上例中,我們使用 v
為弱值賦能(類似於使用 k
來取得鍵值)。在 do
區塊中將測試值 someval
設定為區域變數的目的,是為了讓我們稍後使用 collectgarbage
呼叫來強制回收變數。請注意,使用文字(例如字串或數字)來取得 someval
是無效的,因為文字絕不會被垃圾回收。
請注意,一旦使用某張表為元資料,就不能變更元資料的 __mode
屬性(有關確切限制,請參閱參考手冊 [1] 的第 2.10.2 節)。換句話說,嘗試透過變更元資料的 __mode
屬性將表變更為弱表的以下程式碼是錯誤的
getmetatable(t).__mode = 'v'
__mode
屬性。因此,以下風格(建立一張空表,然後立即賦予元資料)才是正確的
local weaktable = setmetatable({}, {__mode="k"})
弱表通常用於您希望註解變數而不會變更它們的情況。例如,您可能想為物件提供可在列印時使用之名稱。在這種情況下,您不希望物件已命名這項事實會阻止它被垃圾回收,因此您會想要使用具有弱鍵(物件)的表
local names = setmetatable({}, {__mode = "k"}) -- with the example below, this would be a local function function name(obj, str) names[obj] = tostring(str) return obj end -- keep the original print function available local _print = print function print(...) for i = 1, arg.n do local name = names[arg[i]] if name then arg[i] = name end end _print(unpack(arg)) end
透過自動為全域變數命名,您可能希望將其用於除錯。您可以透過將簡單的元方法(請參閱 MetamethodsTutorial)新增至全域變數表來做到這一點
local globalsmeta = {} local nameable_type = {["function"] = true, userdata = true, thread = true, table = true} function globalsmeta:__newindex(k, v) if nameable_type[type(v)] then name(v, k) end rawset(self, k, v) end setmetatable(_G, globalsmeta)
注意我們如何避免做一連串複雜的 if then elseif...
敘述,而是利用一個常數表對這個值的類型做單一檢查。
對於高階教學範例的讀者來說,__newindex
的函數文字可以寫成如下
rawset(self, k, nameable_type[type(v)] and name(v, k) or v)