lua 弱引用table

前言

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
<think>好的,用户想了解Lua中强引用和弱引用的区别及使用场景。首先,我需要回顾一下Lua中的引用机制。强引用是最常见的引用类型,只要存在强引用,对象就不会被垃圾回收器回收。而弱引用则不同,即使存在弱引用,如果对象没有其他强引用,垃圾回收时会被回收。 接下来,我得确认用户的问题重点。他们可能是在处理内存管理问题,或者遇到了对象无法释放的情况,想通过弱引用来优化内存使用。需要详细解释两者的区别,并举例说明使用场景,比如缓存、对象管理、监听器模式等。 还要注意Lua弱引用表的设置方式,通过元表的__mode字段,可以设置为"k"、"v"或"kv"。这部分需要具体说明,确保用户知道如何正确声明弱引用表。 另外,用户提供的引用资料中提到了弱引用表的使用和垃圾回收机制,需要将这些信息整合到回答中,确保引用正确。例如,引用[3]提到弱引用表不会增加引用计数,这需要强调。 最后,生成相关问题时要围绕Lua内存管理、弱引用的具体实现和其他相关机制,比如元表的使用。确保问题对用户进一步学习有帮助。</think>### Lua强引用与弱引用的区别及使用场景 #### 一、核心区别 1. **强引用** - 对象被强引用时,其生命周期由引用计数决定 - 即使内存不足,只要有强引用存在,对象就不会被垃圾回收器(GC)回收 - 示例:普通表的键值对默认都是强引用 ```lua local strongTable = {} strongTable.key = {} -- 创建强引用关联 ``` 2. **弱引用** - 通过弱引用表(weak table)实现,需显式声明 - 不会阻止GC回收对象,当对象仅被弱引用时会被自动回收 - 声明方式:通过元表设置`__mode`字段 ```lua local weakTable = {} setmetatable(weakTable, {__mode = "k"}) -- 声明键为弱引用 ``` #### 二、技术特性对比 | 特性 | 强引用表 | 弱引用表 | |--------------|--------------------------|-----------------------------| | GC行为 | 阻止对象回收 | 允许对象回收[^3] | | 内存管理 | 需要手动解除引用 | 自动清理失效条目 | | 典型应用 | 常规数据存储 | 缓存/临时数据管理 | | 引用计数影响 | 增加引用计数 | 不影响引用计数[^3] | | 声明方式 | 默认 | 需设置元表`__mode`字段 | #### 三、使用场景 1. **缓存系统** 适合存储临时计算结果,当内存不足时自动释放旧缓存: ```lua local cache = {} setmetatable(cache, {__mode = "v"}) -- 值弱引用 function getBigData(id) if not cache[id] then cache[id] = computeExpensiveData(id) end return cache[id] end ``` 2. **对象管理** 管理临时对象时避免内存泄漏: ```lua local tempObjects = setmetatable({}, {__mode = "k"}) function createTempObject() local obj = {} tempObjects[obj] = true return obj end ``` 3. **监听器模式** 自动移除失效的监听器引用: ```lua local listeners = setmetatable({}, {__mode = "k"}) function addListener(target, callback) listeners[callback] = target end ``` #### 四、注意事项 1. `__mode`字段可选值: - `"k"`:键为弱引用 - `"v"`:值为弱引用 - `"kv"`:键值均为弱引用 2. 弱引用表条目会在GC时自动清除 3. 不应将系统关键对象存入弱引用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值