弱表指南

lua-users home
wiki

在閱讀本指南之前,理解垃圾回收會有所幫助。 GarbageCollectionTutorial 提供了簡介。

弱連結

在使用垃圾回收的電腦語言,例如 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)

最近的變更 · 偏好設定
修改 · 歷史記錄
最後編輯於 2014 年 1 月 5 日下午 12:40 GMT (差異)