Lua 基础之弱引用 table

男娘i 2022-06-12 07:27 405阅读 0赞

弱引用 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

    1. print(k, v)

    end

    print(“—————“)

    — 强制垃圾回收
    collectgarbage()

    for k, v in pairs(table) do

    1. print(k, v)

    end

看一下输出结果

  1. table: 005FC1A8 20
  2. table: 005FC158 10
  3. x table: 005FC180
  4. y hello ----------
  5. table: 005FC1A8 20
  6. y hello

原来的 table 共有 4 个数据,强制垃圾回收后只剩下两个。第二个 key 定义的时候会覆盖掉第一个,因此第一个 key 所指的对象就没有引用了;另外 x 和 y 的值也没有引用,但 y 的值是字符串,其 key-value 类型都是值类型,因此不会被回收

备忘录 memoize

备忘录是一种以空间换时间的机制,调用一个函数时,将某些参数的计算结果保存到一个备忘录 table 中,下次调用函数传进来同样的参数时,直接从备忘录 table 中取值。这种方式有个问题就是很容易产生垃圾数据,某些结果可能只使用一次,后面不会再用到了,但这些结果被备忘录 table 引用着,因此不会被回收。解决的方法就是使用弱引用 table。

  1. local color = {}
  2. setmetatable(color, {__mode = "v"})
  3. create_color = function(r, g, b)
  4. local key = r .. "-" .. g .. "-" .. b
  5. local res = color[key]
  6. if res == nil then
  7. res = {red = r, green = g, blue = b}
  8. color[key] = res
  9. end
  10. return res
  11. end
  12. local color1 = create_color(255, 255, 255)
  13. local color2 = create_color(255, 255, 255)
  14. print(color1 == color2) -- true

对象属性

有时候需要将属性绑定到一个对象,如果是对象是 table 和话可以定义几个 key 来保存这些属性,但如果是其它对象或者不想打乱 table 的结构的话就必须另外找方法了。比如一个函数要绑定一个名字属性,一个数组要绑定一个大小属性,一个表要绑定一个默认值属性。有一种解决方案就是使用一个外部 table 来保存对象和属性之间的对应关系,但这样的话这些对象就被多引用了一次,可能导致永远无法回收,这时很自然地就想到使用弱引用 table。这里使用弱引用 key,即当对象没有引用时回收,而不是当属性没引用时回收

  1. local property = {}
  2. local mt = {__mode = "k"}
  3. setmetatable(property, mt)
  4. local array = {
  5. 1, 2, 3, 4, 5}
  6. -- 保存对象属性
  7. property[array] = {size = 10}
  8. for i = 1, property[array].size do
  9. print(i, array[i])
  10. end
  11. array = nil
  12. collectgarbage()
  13. print(#property) -- 0

回顾 table 的默认值

在介绍元表的时候我们已经讲过可以使用 table 的 __index 元方法来设置 table 的默认值,前面介绍了两种方式,这里再介绍两种

使用弱引用 table 保存每个 table 和它的默认值

  1. local defaults = {}
  2. setmetatable(defaults, {__mode = "k"})
  3. local mt = {__index = function(t)
  4. return defaults[t]
  5. end}
  6. set_default = function(t, d)
  7. defaults[t] = d
  8. setmetatable(t, mt)
  9. end
  10. local t = {}
  11. set_default(t, 100)
  12. print(t.x)

这种方式把表的默认值保存到一个外部 table 中,然后所有的 table 共享一个元表,这个元表的 __index 元方法从外部 table 中获取默认值。把外部 table 设计成弱引用 table,这样垃圾回收器才能正常回收 table 对象

把 table 的元表设计成备忘录

  1. local metas = {}
  2. setmetatable(metas, {__mode = "k"})
  3. local set_default = function(t, d)
  4. local mt = metas[d]
  5. if mt == nil then
  6. mt = {__index = function()
  7. return d
  8. end}
  9. metas[d] = mt
  10. end
  11. setmetatable(t, mt)
  12. end
  13. local t = {}
  14. set_default(t, 666)
  15. local tt = {}
  16. set_default(tt, 666)
  17. 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 或者默认值很少重复,则使用第三种方式

发表评论

表情:
评论列表 (有 0 条评论,405人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Lua 表的引用

           最近被问到一个问题,什么是lua表的弱引用,之前看过lua程序设计第四版,但是当时不记得了,并不能回答出来。之后做了简单查阅,作此记录。        lua的

    相关 lua-table

    table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数字、字典等。 Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能

    相关 Lua 基础引用 table

    弱引用 table lua 的垃圾回收器只会回收没有引用的对象,有些时候并不能回收程序员认为的垃圾。比如数组里的元素在其它地方已经没有引用了,但因为还在数组中,因此