
Lua 上值(UpValue)
一、核心结论
UpValue(上值)就是:闭包函数引用的、定义在它外部作用域中的变量。
例如:
lua
function CreateCounter()
local count = 0
return function()
count = count + 1
return count
end
end
这里内部匿名函数使用了外层的 count。
虽然 CreateCounter() 执行结束后,count 按普通局部变量来说应该离开作用域,但这个变量仍然被返回的闭包使用。
这个 count 就是:
UpValue
可以简单理解成:
text
外层局部变量
↓
被内部函数引用
↓
变成闭包的 UpValue
↓
外层函数结束后依然存活
二、为什么需要 UpValue?
主要是为了实现 Lua 的闭包(Closure)。
例如:
lua
function CreateCounter()
local count = 0
return function()
count = count + 1
return count
end
end
local counter = CreateCounter()
print(counter()) -- 1
print(counter()) -- 2
print(counter()) -- 3
这里:
lua
count
属于 CreateCounter 的局部变量。
但 CreateCounter() 已经执行完了,count 仍然需要保存。
因此 Lua 会让这个变量继续存活,并由闭包持有。
三、UpValue 与普通局部变量的区别
普通局部变量:
lua
function Test()
local a = 10
end
a 只在 Test 的生命周期内有效。
而 UpValue:
lua
function Test()
local a = 10
return function()
print(a)
end
end
这里 a 被内部函数引用,因此它需要在外层函数结束后继续存在。
可以理解为:
text
普通局部变量:
函数栈帧
┌──────────┐
│ a = 10 │
└──────────┘
函数结束 → 消失
UpValue:
函数栈帧
┌──────────┐
│ a = 10 │
└────┬─────┘
│
▼
UpValue
▲
│
Closure
四、UpValue 和 Closure 的关系
这是面试最容易问的一组概念。
Closure 是什么?
闭包可以简单理解成:
函数 + 它所引用的外部变量
例如:
lua
function Create()
local x = 100
return function()
return x
end
end
可以理解为:
text
Closure
├── Function
└── UpValue
└── x = 100
所以:
UpValue 是 Closure 保存外部变量的机制。
五、UpValue 的一个重要特点:共享
这个知识点很容易出面试题。
例如:
lua
function Create()
local value = 0
local function Add()
value = value + 1
end
local function Get()
return value
end
return Add, Get
end
然后:
lua
local add, get = Create()
add()
add()
print(get()) -- 2
为什么 get() 能拿到 2?
因为:
text
Add
↓
共同引用
↓
UpValue(value)
↑
共同引用
↑
Get
Add 和 Get 共享的是同一个 UpValue,而不是各自复制了一份 value。
这点在理解 Lua 闭包实现时非常重要。
六、UpValue 的"开放"和"闭合"
如果面试官问得比较深入,可能会问:
Lua 底层是怎么实现 UpValue 的?
可以简单分成两个状态:
1. Open UpValue
外层函数还在执行时:
text
局部变量还在栈上
↓
UpValue 指向栈上的变量
例如:
text
Stack
┌──────────┐
│ x = 100 │ ← UpValue 指向这里
└──────────┘
2. Closed UpValue
外层函数执行结束后:
text
栈上的变量即将失效
↓
把变量复制到堆上的 UpValue
↓
Closure 继续引用
概念上:
text
执行中:
Stack
↑
UpValue
↑
Closure
外层函数结束:
UpValue
↓
独立保存 x
↑
Closure
所以:
UpValue 可以理解为把外部局部变量的生命周期延长到闭包仍然需要它的时候。
七、为什么 Unity / Lua 项目要关注 UpValue?
主要是两个方面。
1. 闭包产生的额外开销
例如高频代码里:
lua
for i = 1, 10000 do
local callback = function()
print(i)
end
end
会不断创建闭包以及相关 UpValue,可能带来:
text
内存分配
+
Lua GC 压力
所以在 Unity 游戏中,Update、循环、战斗高频逻辑里不要无意义地大量创建闭包。
2. UpValue 会影响对象生命周期
例如:
lua
function Create()
local hugeTable = CreateHugeTable()
return function()
return hugeTable
end
end
只要返回的闭包还存在:
text
Closure
↓
UpValue
↓
hugeTable
那么 hugeTable 仍然可能被引用。
因此:
一个闭包如果无意中持有大型对象,会导致对象不能及时被 GC 回收。
这在大型 Lua 项目中属于比较典型的内存问题。
八、面试高频追问
Q1:UpValue 是什么?
闭包引用的外层作用域中的变量,就是 UpValue。
Q2:UpValue 和 Closure 有什么关系?
Closure 是函数和相关环境的组合,UpValue 是 Closure 用来保存外部变量引用的机制。
Q3:为什么外层函数执行完,UpValue 还存在?
因为被闭包引用的局部变量生命周期被延长了。Lua 会让它脱离原来的栈生命周期,继续由 UpValue 持有。
Q4:多个闭包会共享 UpValue 吗?
会。同一个外层局部变量被多个闭包引用时,它们可以共享同一个 UpValue,因此一个闭包修改变量,另一个闭包也能看到修改结果。
Q5:UpValue 会造成 GC 问题吗?
会。大量创建闭包会增加 Lua 对象和 GC 压力;如果闭包长期持有大型 table 或对象,还可能延长这些对象的生命周期,导致内存占用增加。
面试记忆关键词
闭包 → 外部变量 → UpValue → 延长生命周期 → 多闭包共享 → Open/Closed → GC
🎬 博客主页:https://xiaoy.blog.csdn.net
🎥 本文由 呆呆敲代码的小Y 原创 🙉
🎄 学习专栏推荐:Unity系统学习专栏
🌲 游戏制作专栏推荐:游戏制作
🌲Unity实战100例专栏推荐:Unity 实战100例 教程
🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📆 未来很长,值得我们全力奔赴更美好的生活✨
------------------❤️分割线❤️-------------------------




资料白嫖,技术互助
| 学习路线指引(点击解锁) | 知识定位 | 人群定位 |
|---|---|---|
| 🧡 Unity系统学习专栏 | 入门级 | 本专栏从Unity入门开始学习,快速达到Unity的入门水平 |
| 💛 Unity实战类项目 | 进阶级 | 计划制作Unity的 100个实战案例!助你进入Unity世界,争取做最全的Unity原创博客大全。 |
| ❤️ 游戏制作专栏 | 难度偏高 | 分享学习一些Unity成品的游戏Demo和其他语言的小游戏! |
| 💚 游戏爱好者万人社区 | 互助/吹水 | 数万人游戏爱好者社区,聊天互助,白嫖奖品 |
| 💙 Unity100个实用技能 | Unity查漏补缺 | 针对一些Unity中经常用到的一些小知识和技能进行学习介绍,核心目的就是让我们能够快速学习Unity的知识以达到查漏补缺 |
