一开始,应该要记住的是:
(1)weak表中的引用是弱引用(weakreference),弱引用不会导致对象的引用计数变化,换言之,如果一个对象只有弱引用指向它,那么gc会自动回收该对象的内存
(2)只有对象才可以从一个weak table中被收集。比如数字和布尔值类型的值,都是不会被收集的
(3)弱表没有引用计数的概念,例如table key是弱引用的, 当你的key为nil,就会被回收
参考 http://blog.csdn.net/shimazhuge/article/details/40310233 中的例子:
例子1:
weakTable = {}
weakTable[1] = function() print("i am the first element") end
weakTable[2] = function() print("i am the second element") end
weakTable[3] = {10, 20, 30}
setmetatable(weakTable, {__mode = "v"}) -- 设置为弱表
print(table.getn(weakTable)) -->3
ele = weakTable[1] -- 给第一个元素增加一个引用
collectgarbage()
print(table.getn(weakTable)) -->1,第一个函数引用为1,不能gc
ele = nil -- 释放引用
collectgarbage()
print(table.getn(weakTable)) -->0,没有其他引用了,全部gc
注意,这里表的索引为number,,不会被弱表收集
t = {};
-- 使用一个table作为t的key值
key1 = {name = "key1"};
t[key1] = 1;
key1 = nil;
-- 又使用一个table作为t的key值
key2 = {name = "key2"};
t[key2] = 1;
key2 = nil;
-- 强制进行一次垃圾收集
collectgarbage();
for key, value in pairs(t) do
print(key.name .. ":" .. value);
end
--输出
--key1:1
--key2:1
这里因为不是弱表,所以应该看引用计数。拿key1来说,key1 = {name = "key1"}引用了一次,t[key1] = 1;这里又引用了一次,当key1 = nil时,引用计数还是1.因此不会被释放。key2一样。
例子3:
t = {};
-- 给t设置一个元表,增加__mode元方法,赋值为“k”
setmetatable(t, {__mode = "k"});
-- 使用一个table作为t的key值
key1 = {name = "key1"};
t[key1] = 1;
key1 = nil;
-- 又使用一个table作为t的key值
key2 = {name = "key2"};
t[key2] = 1;
key2 = nil;
-- 强制进行一次垃圾收集
collectgarbage();
for key, value in pairs(t) do
print(key.name .. ":" .. value);
end
--输出 为空
这就是弱引用table的其中一种,给table添加__mode元方法,如果这个元方法的值包含了字符串”k”,就代表这个table的key都是弱引用的。
一旦其他地方对于key值的引用取消了(设置为nil),那么,这个table里的这个字段也会被删除。
通俗地说,因为t的key被设置为弱引用,所以,执行t[key1] = 1后,t中确实存在这个字段。随后,又执行了key1 = nil,此时,除了t本身以外,就没有任何地方对key1保持引用,所以t的key1字段也会被删除。
例子4:(把上面弱引用改成v)
t = {};
-- 给t设置一个元表,增加__mode元方法,赋值为“k”
setmetatable(t, {__mode = "v"});
-- 使用一个table作为t的key值
key1 = {name = "key1"};
t[key1] = 1;
key1 = nil;
-- 又使用一个table作为t的key值
key2 = {name = "key2"};
t[key2] = 1;
key2 = nil;
-- 强制进行一次垃圾收集
collectgarbage();
for key, value in pairs(t) do
print(key.name .. ":" .. value);
end
输出结果为:
key1:1
key2:1
这时对应的是value,所以会有输出。
下面在从上面的链接,记录下应用:
weak表的简单应用——记忆函数
一个相当普遍的编程技术是用空间来换取时间。你可以通过记忆函数结果来进行优化,当你用同样的参数再次调用函数时,它可以自动返回记忆的结果。
想像一下一个通用的服务器,接收包含Lua代码的字符串请求。每当它收到一个请求,它调用loadstring加载字符串,然后调用函数进行处理。然而,loadstring是一个“巨大”的函数,一些命令在服务器中会频繁地使用。不需要反复调用loadstring和后面接着的closeconnection(),服务器可以通过使用一个辅助table来记忆loadstring的结果。在调用loadstring之前,服务器会在这个table中寻找这个字符串是否已经有了翻译好的结果。如果没有找到,那么(而且只是这个情况)服务器会调用loadstring并把这次的结果存入辅助table。我们可以将这个操作包装为一个函数:
- local result = {}
- function mem_loadstring(s)
- if result[s] then
- return result[s]
- else
- local res = loadstring(s)
- result[s] = res
- return res
- end
- end
这个方案的存储消耗可能是巨大的。尽管如此,它仍然可能会导致意料之外的数据冗余。尽管一些命令一遍遍的重复执行,但有些命令可能只运行一次。渐渐地,这个table积累了服务器所有命令被调用处理后的结果;早晚有一天,它会挤爆服务器的内存。一个weak table提供了对于这个问题的简单解决方案。如果这个结果表中有weak值,每次的垃圾收集循环都会移除当前时间内所有未被使用的结果(通常是差不多全部):
setmetatable(results, {__mode =\"v\"}) -- make values weak
事实上,因为table的索引下标经常是字符串式的,如果愿意,我们可以将table全部置weak:
setmetatable(results, {__mode =\"kv\"})
记忆技术在保持一些类型对象的唯一性上同样有用.例如,假如一个系统将通过tables表达颜色,通过有一定组合方式的红色,绿色,蓝色。一个自然颜色调色器通过每一次新的请求产生新的颜色:
- function createRGB (r, g, b)
- return {red = r, green = g, blue = b}
- end
使用记忆技术,我们可以将同样的颜色结果存储在同一个table中。为了建立每一种颜色唯一的key,我们简单的使用一个分隔符连接颜色索引下标:
- local res = {}
- setmetatable(res,{__mode = "v"})
- function createRGB(r, g, b)
- local key = r .. "-" .. g .. "-" .. b
- if res[key] then
- return res[key]
- else
- local newcolor = {red = r,green = g,blue = b}
- res[key] = newcolor
- return newcolor
- end
- end
一个有趣的后果就是,用户可以使用这个原始的等号运算符比对操作来辨别颜色,因为两个同时存在的颜色通过同一个的table来表达。要注意,同样的颜色可能在不同的时间通过不同的tales来表达,因为垃圾收集器一次次的在清理结果table。然而,只要给定的颜色正在被使用,它就不会从结果中被移除。所以,任何时候一个颜色在同其他颜色进行比较的时候存活的够久,它的结果镜像也同样存活。
weak表的简单应用——关联对象属性
weak表的简单应用——带有默认值得表
- --[[在第一种解决方案中,我们使用weak table来将默认vaules和每一个table相联系:
- 使用weak table来将默认vaules和每一个table相联系--]]
- local defaults = {}
- setmetatable(defaults,{__mode = "k"})
- local mt = {__index = function(t) return defaults[t] end}
- function setDefault(t,d)
- defaults[t] = d
- setmetatable(t,mt)
- end
- --[[如果默认值没有weak的keys,它就会将所有的带有默认值的tables设定为永久存在。在第二种方法中,
- 我们使用不同的metatables来保存不同的默认值,但当我们重复使用一个默认值的时候,重用同一个相同
- 的metatable。这是一个典型的记忆技术的应用:--]]
- local metas = {}
- setmetatable(metas,{__mode = "v"})
- function setDefault(t,d)
- local mt = metas[d]
- if mt == nil then
- mt = {__index = function() return d end}
- metas[d] = mt --memoize
- end
- setmetatable(t,mt)
- end
这种情况下,我们使用weak vaules,允许将不会被使用的metatables可以被回收。
把这两种方法放在一起,哪个更好?通常,取决于具体情况。它们都有相似的复杂性和相似的性能。第一种方法需要在每个默认值的tables中添加一些文字(一个默认的入口)。第二种方法需要在每个不同的默认值加入一些文字(一个新的表,一个新的闭包,metas中新增入口)。所以,如果你的程序有数千个tables,而这些表只有很少数带有不同默认值的,第二种方法显然更优秀。另一方面,如果只有很少的tabels可以共享相同的默认vaules,那么你还是用第一种方法吧。
以上大部分内容来自于互联网,有兴趣可参照: