lua采用自动垃圾回收机制。但有的时候,自动垃圾回收并不是那么智能。
以下两种情况需要我们手动控制内存:
1.全局的对象,如放在全局变量中的table,当不用的时候需要手动设置为nil。
2.当一个对象放在一个table中,如果这个对象在其它任何地方都未使用,但是却被当前都容器table所引用着,这会导致这个对象无法得到释放。
Lua提供了弱引用table,可以释放gc忽略释放的对象。
local mt = {__index = {foo = "foo", bar = "bar"}}
local t_a = {}
setmetatable(t_a, mt)
--key是对象形式
local key
key = {}
local v = 1
t_a[key] = v
key = {} --覆盖了第一个key,第一个key其它地方没有引用了,这时候可以被释放
v = 2
t_a[key] = v
collectgarbage() --主动触发一次gc
for k, v in pairs(t_a) do
print(v) -- 1 2
end
可以看到,第一个key,在没有任何地方用到的时候,被t_a引用了,所以无法被gc自动释放。
添加弱引用标记:
local mt = {__index = {foo = "foo", bar = "bar"}, __mode = "k"} --标记key是弱引用
--其它代码不变
for k, v in pairs(t_a) do
print(v) -- 2
end
断点后如图所示:
插入两个值后:
执行gc后:
发现,第一个key(table)已经被销毁了。
同样,如果插入table的key是值类型,而value是对象类型,可以这么设置弱引用:
local mt = {__index = {foo = "foo", bar = "bar"}, __mode = "v"} --标记value是弱引用
也可以同时设置:
local mt = {__index = {foo = "foo", bar = "bar"}, __mode = "kv"}
以上是lua的gc会回收弱引用table中的对象,那table的值怎么处理的?
首先先不设置弱引用:
local mt = {__index = {foo = "foo", bar = "bar"}}
local t_a = {}
setmetatable(t_a, mt)
local key = "foo"
v = 3
t_a[key] = v
key = "foo"
v = 4
t_a[key] = v
collectgarbage()
for k, v in pairs(t_a) do
print(v) --4
end
可以看到,v=3直接被替换掉了。说明:值类型的数据可以直接被gc。(不主动调用gc,lua的gc也会自动回收掉)
如果是string类型:
local mt = {__index = {foo = "foo", bar = "bar"}}
local t_a = {}
setmetatable(t_a, mt)
local key = "b" .. "ar"
v = 5
t_a[key] = v
key = "b" .. "ar" --说明此时并没有重新创建一个字符串
v = 6
t_a[key] = v
collectgarbage()
for k, v in pairs(t_a) do
print(v) --6
end
同样,不设置弱引用也会被gc,就没有必要设置弱引用。
来一个简单但感觉不是很恰当的例子
注:function.lua代码是从cocos-2dx-lua里面拷出来的,文章最后会贴部分代码出来。
require "functions"
local animal = class("animal")
function animal:ctor(data)
if not data then return end
self._id = data.id
self._name = data.name
print("animal->ctor", self._name, self._id)
end
function animal:draw()
print(string.format("animal print:id->%s, name->%s", self._id, self._name))
end
local cat = class("cat", animal)
function cat:ctor(data)
cat.super.ctor(self, data)
self._age = 0
end
function cat:setAge(age)
self._age = age
end
local catList = {}
setmetatable(catList, {__mode="k"})
local cat = cat.new{id=1, name="bob"}
cat:setAge(3)
catList[cat] = {notes="bob is a male cat"}
--模拟一次bob cat的引用
--打开此处,这个对象在其他地方有引用,所以不会被gc
--[[local otherList = {}
table.insert(otherList, cat)--]]
cat = cat.new{id=2, name="tom"}
cat:setAge(4)
catList[cat] = {notes="tom is female cat"}
collectgarbage()
for k, v in pairs(catList) do
print(k._id, v.notes) --2 tom is female cat
end
这个例子,key和value都是table类型。
key/value都是table,这也是比较常用的模式:为一个对象添加一些其他属性,但不会影响到对象本身的数据。
比如:对象本身的数据从其他地方传进来,在此处调用处理数据的时候,不希望修改对象本身的数据,但又想给对象添加一些属性。这时,可以让对象本身当作key,额外的属性设置为value,放到一张表中。
function.lua的部分代码,封装了class方法:
function class(classname, super)
local superType = type(super)
local cls
if superType ~= "function" and superType ~= "table" then
superType = nil
super = nil
end
if superType == "function" or (super and super.__ctype == 1) then
-- inherited from native C++ Object
cls = {}
if superType == "table" then
-- copy fields from super
for k,v in pairs(super) do cls[k] = v end
cls.__create = super.__create
cls.super = super
else
cls.__create = super
cls.ctor = function() end
end
cls.__cname = classname
cls.__ctype = 1
function cls.new(...)
local instance = cls.__create(...)
-- copy fields from class to native object
for k,v in pairs(cls) do instance[k] = v end
instance.class = cls
instance:ctor(...)
return instance
end
else
-- inherited from Lua Object
if super then
cls = {}
setmetatable(cls, {__index = super})
cls.super = super
else
cls = {ctor = function() end}
end
cls.__cname = classname
cls.__ctype = 2 -- lua
cls.__index = cls
function cls.new(...)
local instance = setmetatable({}, cls)
instance.class = cls
instance:ctor(...)
return instance
end
end
return cls
end