Lua 基础之弱引用 table
2017-07-19 09:58
501 查看
弱引用 table
lua 的垃圾回收器只会回收没有引用的对象,有些时候并不能回收程序员认为的垃圾。比如数组里的元素在其它地方已经没有引用了,但因为还在数组中,因此垃圾回收器并不会去回收它弱引用 table 告诉回收器一个元素在 table 中的引用不应该阻止它的回收。如果一个对象的引用都是弱引用,那回收器就会回收这个对象
弱引用 table 有三种:弱引用 key,弱引用 value 和弱引用 key-value。设置 table 的元表的 __mode 元字段就可以设置一个表为弱引用表,__mode = “k” 表示是弱引用 key 表,__mode = “v” 表示是弱引用 value 表,__mode = “kv” 表示是弱引用 key-value 表
lua 只会回收弱引用 table 中的对象,对于数字、布尔、字符串此类的值是不会回收的
table = {} setmetatable(table, {__mode = "kv"}) key = {} table[key] = 10 key = {} table[key] = 20 table.x = {} table.y = "hello" for k, v in pairs(table) do print(k, v) end print("----------") -- 强制垃圾回收 collectgarbage() for k, v in pairs(table) do print(k, v) end
看一下输出结果
table: 005FC1A8 20 table: 005FC158 10 x table: 005FC180 y hello ---------- table: 005FC1A8 20 y hello
原来的 table 共有 4 个数据,强制垃圾回收后只剩下两个。第二个 key 定义的时候会覆盖掉第一个,因此第一个 key 所指的对象就没有引用了;另外 x 和 y 的值也没有引用,但 y 的值是字符串,其 key-value 类型都是值类型,因此不会被回收
备忘录 memoize
备忘录是一种以空间换时间的机制,调用一个函数时,将某些参数的计算结果保存到一个备忘录 table 中,下次调用函数传进来同样的参数时,直接从备忘录 table 中取值。这种方式有个问题就是很容易产生垃圾数据,某些结果可能只使用一次,后面不会再用到了,但这些结果被备忘录 table 引用着,因此不会被回收。解决的方法就是使用弱引用 table。local color = {} setmetatable(color, {__mode = "v"}) create_color = function(r, g, b) local key = r .. "-" .. g .. "-" .. b local res = color[key] if res == nil then res = {red = r, green = g, blue = b} color[key] = res end return res end local color1 = create_color(255, 255, 255) local color2 = create_color(255, 255, 255) print(color1 == color2) -- true
对象属性
有时候需要将属性绑定到一个对象,如果是对象是 table 和话可以定义几个 key 来保存这些属性,但如果是其它对象或者不想打乱 table 的结构的话就必须另外找方法了。比如一个函数要绑定一个名字属性,一个数组要绑定一个大小属性,一个表要绑定一个默认值属性。有一种解决方案就是使用一个外部 table 来保存对象和属性之间的对应关系,但这样的话这些对象就被多引用了一次,可能导致永远无法回收,这时很自然地就想到使用弱引用 table。这里使用弱引用 key,即当对象没有引用时回收,而不是当属性没引用时回收local property = {} local mt = {__mode = "k"} setmetatable(property, mt) local array = {1, 2, 3, 4, 5} -- 保存对象属性 property[array] = {size = 10} for i = 1, property[array].size do print(i, array[i]) end array = nil collectgarbage() print(#property) -- 0
回顾 table 的默认值
在介绍元表的时候我们已经讲过可以使用 table 的 __index 元方法来设置 table 的默认值,前面介绍了两种方式,这里再介绍两种使用弱引用 table 保存每个 table 和它的默认值
local defaults = {} setmetatable(defaults, {__mode = "k"}) local mt = {__index = function(t) return defaults[t] end} set_default = function(t, d) defaults[t] = d setmetatable(t, mt) end local t = {} set_default(t, 100) print(t.x)
这种方式把表的默认值保存到一个外部 table 中,然后所有的 table 共享一个元表,这个元表的 __index 元方法从外部 table 中获取默认值。把外部 table 设计成弱引用 table,这样垃圾回收器才能正常回收 table 对象
把 table 的元表设计成备忘录
local metas = {} setmetatable(metas, {__mode = "k"}) local set_default = function(t, d) local mt = metas[d] if mt == nil then mt = {__index = function() return d end} metas[d] = mt end setmetatable(t, mt) end local t = {} set_default(t, 666) local tt = {} set_default(tt, 666) print(t.x, tt.y)
这种方式会给每个 table 设计一个新的元表,然后把默认值-元表保存到外部 table 中,下次如果一个新的 table 的默认值在外部 table 中可以找到,则使用之前定义的元表而不新创建元表
总结
到目前为止设置 table 的默认值共有四种方式,共同的思想是使用元表的 __index 元方法来返回默认值,不同的点有是独立元表还是共享元表,默认值存放在哪里1. 每个 table 独立一个 metatable,由 __index 元方法直接返回默认值
2. 所有 table 共享一个 metatable,默认值存放在每个 table 本身,__index 元方法从 table 中取默认值返回
3. 所有 table 共享一个 metatable,所有默认值存放在一个外部 table 中,__index 元方法从外部 table 中取默认值返回
4. 一个默认值对应一个 metatable,存放在外部 table 中,使用备忘录的方式给每个 table 设置元表
* 最简单的方式是第一种,但这种方式需要的时间和空间都比较大
* 第二种跟第三种很相似,都是共享一个元表,然后保存默认值;这种方式省去了频繁创建元表的时间,空间上也较第一种有改进
* 第四种虽然也是为每个表创建一个元表,但相同的默认值使用了备忘录的机制,空间和时间都比第一种要好
选择: 如果 table 很多且有很多重复的默认值,则使用第四种方式;如果只有个别的 table 或者默认值很少重复,则使用第三种方式
相关文章推荐
- Lua教程之弱引用table
- Lua基础之table详解
- Lua之弱引用table
- cocos2d-x-lua基础系列教程六(lua-table增删改查)
- 0基础lua学习(四)table构造式
- 【笨木头Lua专栏】基础补充09:使用table.concat连接大量字符串
- Lua教程(十三):弱引用table
- 0基础lua学习(十一)table
- lua中表table的拷贝而不是引用的操作方法
- lua中table的基础用法
- 【本·伍德Lua专栏】补充的基础09:使用table.concat将一个大的字符串
- Step By Step(Lua弱引用table)
- lua table引用问题
- Lua基础教程之表(Table)学习笔记
- Lua基础之table详解
- cocos2d-x-lua基础系列教程六(lua-table增删改查)
- Step By Step(Lua弱引用table)
- lua基础【一】dofile引用lua文件
- Lua基础之table详解
- Lua 基础之Weak Table(5)