Lua中的weak表——weak table

  弱表(weak table)是一个颇有意思的东西,像C++/Java等语言是没有的。弱表的定义是:A weak table is a table whose elements are weak references,元素为弱引用的表就叫弱表。有弱引用那么也就有强引用,有引用那么也就有非引用。咱们先要厘这些基本概念:变量、值、类型、对象。程序员

  (1)变量与值:Lua是一个dynamically typed language,也就是说在Lua中,变量没有类型,它能够是任何东西,而值有类型,因此Lua中没有变量类型定义这种东西。另外,Lua中全部的值都是第一类值(first-class values)。编程

  (2)Lua有8种基本类型:nil、boolean、number、string、function、userdata、thread、table。其中Nil就是nil变量的类型,nil的主要用途就是一个全部类型以外的类型,用于区别其余7中基本类型。数组

  (3)对象objects:Tables、functins、threads、userdata。对于这几种值类型,其变量皆为引用类型(变量自己不存储类型数据,而是指向它们)。赋值、参数传递、函数返回等都操做的是这些值的引用,并不产生任何copy行为。函数

   

  Lua的垃圾回收机制:gc是不少语言的常见机制,让程序员拜托复杂易出错的内存管理。优化

  定义:Lua manages memory automatically by running a garbage collector to collect all dead objects (that is, objects that are no longer accessible from Lua). spa

  三点理解:(1)gc自动运行,也能够手动调用;(2)自动收集的目标是引用计数为0的对象;(3)dead objects:不能访问到的对象,没有引用指向它了,固然就是访问不到的了,也就等同于垃圾内存了。code

 

  weak table的定义:对象

  (1)weak表是一个表,它拥有metatable,而且metatable定义了__mode字段;blog

  (2)weak表中的引用是弱引用(weak reference),弱引用不会致使对象的引用计数变化。换言之,若是一个对象只有弱引用指向它,那么gc会自动回收该对象的内存。索引

  (3)__mode字段能够取如下三个值:k、v、kv。k表示table.key是weak的,也就是table的keys可以被自动gc;v表示table.value是weak的,也就是table的values能被自动gc;kv就是两者的组合。任何状况下,只要key和value中的一个被gc,那么这个key-value pair就被从表中移除了( In any case, if either the key or the value is collected, the whole pair is removed from the table)。

  对于普通的强引用表,当你把对象放进表中的时候,就产生了一个引用,那么即便其余地方没有对表中元素的任何引用,gc也不会被回收这些对象。那么你的选择只有两种:手动释放表元素或者让它们常驻内存。

strongTable = {}
strongTable[1] = function() print("i am the first element") end
strongTable[2] = function() print("i am the second element") end
strongTable[3] = {10, 20, 30}

print(table.getn(strongTable))            -- 3
collectgarbage()                        
print(table.getn(strongTable))            -- 3

  可是,在编程环境中,有时你并不肯定手动给一个键值赋nil的时机,而是须要等全部使用者用完之后进行释放,在释放之前,是能够访问这个键值对的。这种时候,weak表就派上用场了。关于weak table的理解,看下面这个小例子:

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

  固然在实际的代码过程当中,咱们不必定须要手动collectgarbage,由于该函数是在后台自动运行的,它有本身的运行周期和规律,对编程者来讲是透明的。

  注意:只有拥有显示构造的对象类型会被自动从weak表中移除,值类型boolean、number是不会自动从weak中移除的。而string类型虽然也由gc来负责清理,可是string没有显示的构造过程,所以也不会自动从weak表中移除,对于string的内存管理有单独的策略。

  基于weak表的简单应用:

  (1)记忆函数:一个至关广泛的编程技术是用空间来换取时间。你能够经过记忆函数结果来进行优化,当你用一样的参数再次调用函数时,它能够自动返回记忆的结果。将函数的输入和输出分别做为key和value放在一个weak table里面,调用函数以前先查看有无现成的结果,有就返回,没有就调用函数,而后将结果存入表中。因为是weak table,此表会按期自动清理掉再也不有引用的键值对。

  (2)关联对象属性:Lua自己使用这种技术来保存数组的大小。table库提供了一个函数来设定数组的大小,另外一个函数来读取数组的大小。当你设定了一个数组的大小,Lua 将这个尺寸保存在一个私有的weak table,索引就是数组自己,而value就是它的尺寸。

  一样的,当咱们须要给任一对象添加一个属性的时候,能够在外部单独作一弱key表,而后以对象为key值,属性值为value。这样便可以方便的访问这个属性,也不影响该对象的释放。并且对象自己没任何修改,能很好的保持对象自己的独立性。

   (3)带有默认值的表:

  有两种实现方法,第一种方法,使用关联对象属性的方法,将表做为key,默认值做为value,存到一个弱key的weak表中:

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

  第二种方法,针对不一样的metatable来进行优化,对于每个具体的默认值,生成一个与之对应的metatable,而后以默认值为key,metatable为value,存到一个弱value的weak表中:

metas = {}
setmetatable(metas, {__mode = "v"})

setdefault = 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

  两种方式各有利弊,第一种方法对于每个table都须要添加一个键值对,可是公用一个metatable。第二种方法须要许多个不一样的metatable,但拥有相同默认值的table共用一个metatable,而且weak表要比第一种方法小。若是你的代码环境中有不少个table,但经常使用默认值只有那么几种,建议选择第二种方法,不然就选择第一种方法。

相关文章
相关标签/搜索