lua 弱表

一开始,应该要记住的是:

(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,,不会被弱表收集


例子2:

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。我们可以将这个操作包装为一个函数:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. local result = {}  
  2. function mem_loadstring(s)  
  3.     if result[s] then  
  4.         return result[s]  
  5.     else  
  6.         local res = loadstring(s)  
  7.         result[s] = res  
  8.         return res  
  9.     end  
  10. end  

这个方案的存储消耗可能是巨大的。尽管如此,它仍然可能会导致意料之外的数据冗余。尽管一些命令一遍遍的重复执行,但有些命令可能只运行一次。渐渐地,这个table积累了服务器所有命令被调用处理后的结果;早晚有一天,它会挤爆服务器的内存。一个weak table提供了对于这个问题的简单解决方案。如果这个结果表中有weak值,每次的垃圾收集循环都会移除当前时间内所有未被使用的结果(通常是差不多全部):

setmetatable(results, {__mode =\"v\"})   -- make values weak

事实上,因为table的索引下标经常是字符串式的,如果愿意,我们可以将table全部置weak:

setmetatable(results, {__mode =\"kv\"}) 

记忆技术在保持一些类型对象的唯一性上同样有用.例如,假如一个系统将通过tables表达颜色,通过有一定组合方式的红色,绿色,蓝色。一个自然颜色调色器通过每一次新的请求产生新的颜色:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. function createRGB (r, g, b)  
  2.     return {red = r, green = g, blue = b}  
  3. end  

使用记忆技术,我们可以将同样的颜色结果存储在同一个table中。为了建立每一种颜色唯一的key,我们简单的使用一个分隔符连接颜色索引下标

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. local res = {}  
  2. setmetatable(res,{__mode = "v"})  
  3. function createRGB(r, g, b)  
  4.     local key = r .. "-" .. g .. "-" .. b  
  5.     if res[key] then  
  6.         return res[key]  
  7.     else  
  8.         local newcolor = {red = r,green = g,blue = b}  
  9.         res[key] = newcolor  
  10.         return newcolor  
  11.     end  
  12. end  

一个有趣的后果就是,用户可以使用这个原始的等号运算符比对操作来辨别颜色,因为两个同时存在的颜色通过同一个的table来表达。要注意,同样的颜色可能在不同的时间通过不同的tales来表达,因为垃圾收集器一次次的在清理结果table。然而,只要给定的颜色正在被使用,它就不会从结果中被移除。所以,任何时候一个颜色在同其他颜色进行比较的时候存活的够久,它的结果镜像也同样存活。

weak表的简单应用——关联对象属性

weak表的简单应用——带有默认值得表

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. --[[在第一种解决方案中,我们使用weak table来将默认vaules和每一个table相联系:  
  2. 使用weak table来将默认vaules和每一个table相联系--]]  
  3. local defaults = {}  
  4. setmetatable(defaults,{__mode = "k"})  
  5. local mt = {__index = function(t) return defaults[t] end}  
  6. function setDefault(t,d)  
  7.     defaults[t] = d  
  8.     setmetatable(t,mt)  
  9. end  
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. --[[如果默认值没有weak的keys,它就会将所有的带有默认值的tables设定为永久存在。在第二种方法中,  
  2. 我们使用不同的metatables来保存不同的默认值,但当我们重复使用一个默认值的时候,重用同一个相同  
  3. 的metatable。这是一个典型的记忆技术的应用:--]]  
  4. local metas = {}  
  5. setmetatable(metas,{__mode = "v"})  
  6.   
  7. function setDefault(t,d)  
  8.     local mt = metas[d]  
  9.     if mt == nil then  
  10.         mt = {__index = function() return d end}  
  11.         metas[d] = mt  --memoize  
  12.     end  
  13.     setmetatable(t,mt)  
  14. end  

这种情况下,我们使用weak vaules,允许将不会被使用的metatables可以被回收。

把这两种方法放在一起,哪个更好?通常,取决于具体情况。它们都有相似的复杂性和相似的性能。第一种方法需要在每个默认值的tables中添加一些文字(一个默认的入口)第二种方法需要在每个不同的默认值加入一些文字(一个新的表,一个新的闭包,metas中新增入口)。所以,如果你的程序有数千个tables,而这些表只有很少数带有不同默认值的,第二种方法显然更优秀。另一方面,如果只有很少的tabels可以共享相同的默认vaules,那么你还是用第一种方法吧。



以上大部分内容来自于互联网,有兴趣可参照:

http://blog.csdn.net/shimazhuge/article/details/40310233

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值