基础知识
Lua有着自己的自动内存管理。程序只需要负责创建对象,而不需要去删除对象。通过垃圾回收机制,lua会自动去删除那些已经成为垃圾的对象。但问题在于,垃圾回收器只能够回收那些它认为是垃圾的东西。比如说栈,栈通常由一个数组和一个表示栈顶的索引来实现。如果弹出一个元素时只是单纯的将索引移动,而元素还保留在数组织中的话,该元素lua是无法回收的。又比如那些存储在全局变量中的对象,即使程序不会再使用到他,lua也不会认为他们是垃圾。这两种情况下,就需要用户自行将这些变量对象赋值为nil,来得以释放。
而一个对象处于数组中无法回收的根本原因就是不管其他地方是否使用他,数组仍然再引用他。
弱引用就是一种这样的机制。用户能用他来告诉Lua一个引用不应该阻碍一个对象的回收。所谓的弱引用,实际上就是一种会被垃圾收集器忽视的引用,而用户却能够通过某种方式访问到他。如果一个对象的所有引用都是弱引用的话,那么Lua就可以回收这个对象了,并且还可以以某种形式来删除这些引用本身。Lua中我们可以通过弱引用table来实现弱引用,一个弱引用table就是一个具有弱引用条目的table。如果一个对象只被一个弱引用table所持有,那么最终Lua是会回收这个对象的。
这里有三种弱引用table,具有弱引用key的table、具有弱引用value的table和同时具有两种弱引用的table。不论是那种类型的弱引用,只要其中的一个key或value被回收了,那么整个条目就会从table中删除。
一个table的弱引用是通过其元表中的__mode字段来决定的。这个字段的值是一个字符串,如果这个字符串中包含了字母k,则表示这个table的key是弱引用的。如果包含了字母v,那么他的value是弱引用的。
a={}
b={__mode="k"}
setmetatable(a,b) --a的key为弱引用
key={} --创建第一个key
a[key]=1
key={} --创建第二个key
a[key]=2
collectgarbage() --强制进行一次垃圾收集
for k,v in pairs(s) do print(v) end
--> 2
这里,第二句key={}会覆盖第一个key,而此时第一个key没有其他地方在引用 此时第一个key就被回收了。
这里需要注意的是lua只会回收弱引用table中的对象。而橡数字和布尔这样的值,是不可回收的。对于一个数字key对应的value,收集器是永远都不会回收他的。除非当他的value被回收,那么k-v都会被删除。对于字符串,从程序员的角度看,字符串就是值而非对象。所以字符串指向的value和数字布尔一样,不会从弱引用中删除
1. memoize(缓存、记忆)
简而言之就是一种用过空间来换时间的操作。例如记录下函数计算的结果,当传来同样的参数时直接返回结果。不过多去赘述。
2. 对象属性
关于弱引用table,还有一项重要的应用是将属性与对象关联起来。可以将对象作为key,对象的属性作为value。
3 .回顾table默认值
之前我们讨论了如何实现具有非nil默认值的table之前还注明了需要有弱引用的支持,这里我们在重新截杀哦一下 他们其实是上述备忘录和对象属性的特殊应用。
第一种做法是使用有一个弱引用table,通过他讲每个table与其默认值关联起来:
local defaults={}
setmetatable(__mode="k")
local mt={__index=function(t) return defaults[y] end}
function setDefault(t,d)
defaluts[t]=d
setmetatable(t,mt)
end
如果没有弱引用key,他将会使具有默认值的table用就再下去。
第二种做法是对每种不同的默认值使用不同的元表。不过只要有重复的默认值,就复用同样的元表。这是,memoize的典型应用。
local metas={}
asetmetatable(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
第一种需要为每个
table的默认值(defalut中的一个条目)使用内存。而第二种做法则需要为每种不同的默认值开一组内存(一个新的table,一个新的closure和metas中的一个条目).因此如果程序中有上千个table和一些默认值,第二种。如果只有很少的table和几个公用的默认值,第一种。