Lua 的 GC 判断"对象是否可回收"看的是引用:只要还有 table 拿着一个对象,它就不会被回收。这带来一个常见死结——你想给一堆对象挂附加数据(缓存计算结果、临时标记),用普通 table 存 cache[obj] = result,结果 obj 被别处销毁了,cache 还死死抓着它,内存永远降不下来。
弱表就是解药:把表的 __mode 元字段设为 "k"(key 弱)、"v"(value 弱)或 "kv",对应位置的引用不计入 GC 判定。对象一旦在其他地方都不可达,它在弱表里的条目会被自动清除。
local cache = setmetatable({}, { __mode = "v" }) -- value 弱引用
cache[player] = computeExpensive(player) -- 玩家对象销毁后
-- cache[player] 条目自动消失,无需手动清理
第一,计算缓存。 给对象附加派生数据(战力值、格式化名称):用弱 value 表缓存,对象活着缓存有效,对象没了缓存自动蒸发,零维护成本。注意 key 用对象本身、value 放结果,而不是反过来。
第二,观察者与事件回调池。 事件总线若用强引用存监听者,忘记反注册就泄漏。把监听者表做成 __mode = "v" 的弱值表,销毁的对象自动从监听列表里蒸发,等于免费的自动反注册(前提是监听者没有其他强引用,用匿名闭包时要留全局句柄)。
第三,对象附加属性袋。 想给引擎传进来的 userdata/外部队象"贴标签"又不能改它的结构:用弱 key 表 attach[obj] = {...},生命周期与对象严格同生共死。
弱表的清理时机由 GC 决定:对象不可达后,条目要等下一轮回收才消失,期间访问可能仍拿到旧值——别把弱表当成"销毁通知器",销毁回调请走显式接口。其次,__mode = "k" 时数字 key 不会被弱化;值类型的数字、布尔、短字符串本身不受弱引用影响(它们不可被回收)。最后,弱表遍历中做增删可能拿到"将死"条目,遍历逻辑要对 nil 值保持宽容。
一个经验法则:给"别人的对象"挂数据用弱 key 表,给自己算的结果做缓存用弱 value 表。记住这句,弱表就不会用歪。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 祖玛教主倒下的瞬间,掉落的裁决之杖归谁?BOSS 的归属规则是打宝生态的宪法:单挑的归属清晰、组队的分配…
实战应用:用在哪里 新手村的前 30 分钟决定一款游戏的留存:玩家在这半小时里学会移动、战斗、拾取、加行会——学会的速度就是…
实战应用:用在哪里 每个功能都爱挂定时器:扫邮件的、刷怪的、发奖的、心跳的——一百个定时器各自为政,调度层的开销与定时器的数…
实战应用:用在哪里 丢弃裁决之杖、解散行会、删除好友——不可逆的操作一旦执行就没有后悔药。高危操作的确认设计是防误的闸门:让…
实战应用:用在哪里 前端的功能开发经常被服务器档在门外:后端的接口没写完,前端只能干等或造假数据。协议 Mock 在本地扮演…
实战应用:用在哪里 私聊是玩家社交的私信箱:交易的对口、好友的寒暄、行会的动员暗号全走私聊。私聊协议的要点是点对点的寻址、离…