20.Lua语法知识总结

  1. 20.总结
    1. 20.1 核心要点速览
      1. 分组、注释和打印
      2. 简单变量类型
      3. 字符串操作
      4. 运算符
      5. 条件分支语句
      6. 循环语句
      7. 函数
      8. 模块与多脚本
      9. 特殊语法
      10. 协同程序
      11. 元表
      12. 面向对象
      13. 自带库
      14. 垃圾回收
      15. 错误处理
      16. Lua版本差异
    2. 20.2 面试题精选
      1. 基础题
        1. 1. Lua 里哪些值会被当成 false?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. and / or 的短路和返回值规则是什么?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. .. 拼接字符串有哪些坑?怎么写更好?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        4. 4. Lua 函数参数个数不匹配会怎样?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      2. 进阶题
        1. 1. #t 为什么“不可靠”?什么时候能用?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. pairs 和 ipairs 的区别是什么?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. require 的缓存机制是什么?怎么强制重新加载?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        4. 4. setmetatable / __index / rawget 分别解决什么问题?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        5. 5. pcall 和 xpcall 怎么选?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      3. 深度题
        1. 1. 用 Lua 实现“类 / 继承 / 多态”时,: 和 . 的差异会引发什么 bug?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. Lua GC 回收的是什么?是不是引用计数?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. LuaJIT 和 Lua 5.3 / 5.4 是什么关系?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        4. 4. Lua 5.1 的 setfenv 和 Lua 5.2 的 _ENV 有什么关系?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        5. 5. Lua 5.4 的 __close 和 __gc 有什么区别?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章

20.总结


20.1 核心要点速览

分组、注释和打印

功能 语法/示例 说明
编辑器分组 --#region / --#endregion 主要给编辑器折叠用,不是 Lua 语言本身的特殊语法
单行注释 -- 注释内容 最常用,适合解释代码、临时屏蔽一行逻辑
多行注释 --[[ 多行注释内容 ]] 多行注释最常见写法;]]----]] --[[ ... ]]
打印输出 print("Hello Lua") 可以传多个参数,调试时建议把变量含义一起打出来
print("playerId:", playerId)
print("当前状态:", state)

简单变量类型

Lua 常见类型有 8 种:

类型 说明 常见场景
nil 空值,未声明变量默认也是 nil 表示没有值、删除 table 字段
boolean true / false 条件判断
number 数值类型,整数和小数都当 number 数值计算
string 字符串,单引号、双引号都可以 文本、日志、配置 key
function 函数也是值 回调、闭包、模块接口
table Lua 最核心的复合结构 数组、字典、对象、配置
userdata 宿主环境暴露给 Lua 的对象 C/C++ 扩展、Unity 对象绑定
thread Lua 协程类型 coroutine

真值规则:

只有 nil 和 false 是假。其它都是真。
if 0 then
    print("0 也是真")
end

if "" then
    print("空字符串也是真")
end

if {} then
    print("空表也是真")
end

判断“是否为 0 / 空字符串 / 空表”要写明确条件,别被 C# / C++ 习惯带偏:

if num ~= 0 then
    print("num 不是 0")
end

if str ~= "" then
    print("str 不是空字符串")
end

表字段赋 nil 一般等于删掉字段:

local t = { id = 1 }

t.id = nil

print(t.id) -- nil

字符串操作

str1 = "双引号字符串"
str2 = '单引号字符串'
str3 = [[
多行字符串
]]

# 是字节长度,不是中文字符数:

local str = "你好"

print(#str) -- UTF-8 下通常是 6

拼接用 ..number 通常可参与拼接,nil 不行:

print("123" .. "456") -- 123456
local value = nil

-- print("value = " .. value) -- 报错
print("value = " .. tostring(value)) -- value = nil
方法 作用 注意
string.upper 转大写 主要针对 ASCII 字母
string.lower 转小写 主要针对 ASCII 字母
string.reverse 字符串反转 按字节反转,中文容易乱码
string.find 查找子串 返回起始和结束索引
string.sub 截取字符串 索引从 1 开始,支持负数
string.rep 重复字符串 适合简单重复拼接
string.gsub 替换字符串 返回新字符串和替换次数
string.byte 字符转 ASCII / 字节值 多字节字符要小心
string.char ASCII / 字节值转字符 可传多个参数

大量拼接、中文长度、富文本截断通常要封装工具函数。

运算符

类型 运算符 说明
算术 + - * / % ^ 加减乘除、取余、幂
比较 == ~= > < >= <= 不等于是 ~=
逻辑 and or not 短路运算,返回原始值,不一定返回布尔
拼接 .. 字符串拼接
位运算 Lua 5.3+:&|~<<>> Lua 5.1 / LuaJIT 项目不要默认可用

Lua 没有 a++a += 1 等写法,要写成 a = a + 1

and / or 返回值:

a and b:a 为假返回 a,a 为真返回 b。
a or b:a 为真返回 a,a 为假返回 b。
local value = input or defaultValue

false 是合法值时不能这么兜底:

if input == nil then
    value = defaultValue
else
    value = input
end

条件分支语句

if 条件1 then
    -- 分支1
elseif 条件2 then
    -- 分支2
else
    -- 分支3
end
  • elseif,不是 else if
  • 只有 nilfalse 是假;0""{} 都是真。
  • 多分支从上往下,命中一个就不再往下。

循环语句

类型 语法 执行逻辑 注意
while while 条件 do ... end 先判断,再执行 条件一开始不成立就一次都不执行
repeat repeat ... until 条件 先执行,再判断 条件为真时退出,和 C# do...while 容易混
数值 for for i = start, stop, step do ... end 按步长变化 步长方向和起止值对不上时,循环不执行
local num = 0

while num < 5 do
    print(num)
    num = num + 1
end
local num = 0

repeat
    print(num)
    num = num + 1
until num > 5
for i = 1, 5, 1 do
    print(i)
end

数值 for:步长为正,变量大于终止值退出;步长为负,变量小于终止值退出;方向不对直接跳过。死循环配合 break

函数

function Add(a, b)
    return a + b
end
local func = function()
    print("匿名函数")
end

函数是值,可赋变量、传参、返回。

情况 结果
少传参数 缺少的位置补 nil
多传参数 多出来的参数被丢弃
参数类型不匹配 Lua 不会提前检查,运行时逻辑自己负责
function Test(a)
    print(a)
end

Test()        -- nil
Test(1, 2, 3) -- 1
function GetResult()
    return true, "ok"
end

local success, message = GetResult()

闭包捕获外部变量并延长其生命周期:

function MakeAdder(x)
    return function(y)
        return x + y
    end
end

local add10 = MakeAdder(10)

print(add10(5)) -- 15

数组、字典、对象、配置都靠 table

local arr = { 1, 2, 3 }

print(arr[1]) -- 1
local player = {
    id = 1001,
    name = "Tom"
}

print(player.id)
print(player["name"])

表赋值是引用传递:

local a = { name = "A" }
local b = a

b.name = "B"

print(a.name) -- B
结论
#t 只适合连续数组,有洞 table 不要依赖结果
pairs 遍历所有键值对,但顺序不保证
ipairs 从 1 开始遍历,遇到中间 nil 通常会停
table.insert(t, value) 尾插
table.insert(t, pos, value) 中间插入,后面的元素会移动
table.remove(t, pos) 删除指定位置,后面的元素会前移
for i = 1, #arr do
    print(arr[i])
end
for i, v in ipairs(arr) do
    print(i, v)
end
for k, v in pairs(player) do
    print(k, v)
end

要固定顺序自己维护 key 列表,别依赖 pairs 顺序。

模块与多脚本

a = 1       -- 全局,进 _G
local a = 1 -- 局部
local TestModule = require("TestModule")
  • 第一次加载执行脚本,可 return 模块表。
  • 结果缓存在 package.loaded
  • 第二次 require 直接返回缓存。
package.loaded["TestModule"] = nil
local TestModule = require("TestModule")

新模块写法:

local M = {}

function M.Test()
    print("Test")
end

return M

老 5.1 代码可能看到 module("TestModule", package.seeall),要认识但新代码不建议继续用。

特殊语法

a, b, c = 1, 2
print(a) -- 1
print(b) -- 2
print(c) -- nil
a, b = b, a
function Test()
    return 1, 2, 3
end

local a, b = Test()

print(a) -- 1
print(b) -- 2

and / or 模拟三目,trueValue 可能是 falsenil 时会失效:

local result = condition and trueValue or falseValue
local result

if condition then
    result = trueValue
else
    result = falseValue
end

协同程序

创建方式 返回类型 执行方式 说明
coroutine.create(func) thread coroutine.resume(co) 返回值里第一个是是否成功
coroutine.wrap(func) function co() 调用更简单,但错误处理没 resume 直观
local co = coroutine.create(function()
    print("协程执行")
end)

coroutine.resume(co)
local co = coroutine.create(function()
    print("A")
    coroutine.yield()
    print("B")
end)

coroutine.resume(co) -- A
coroutine.resume(co) -- B
状态 含义
suspended 挂起,可以继续执行
running 正在执行
dead 执行结束,或者已经不能继续执行
print(coroutine.status(co))

Lua 协程不是系统线程,是“函数执行到一半停住,下次接着跑”。

元表

setmetatable(t, mt)
getmetatable(t)
元方法 触发场景
__tostring 表被转成字符串,比如 print(t)
__call 表被当成函数调用
__add / __sub / __mul / __div 表参与运算
__concat 表参与字符串拼接
__eq 表参与相等比较
__index 访问不存在字段
__newindex 给不存在字段赋值

__index 可以是表或函数:

local mt = {
    __index = {
        name = "default"
    }
}

local t = {}

setmetatable(t, mt)

print(t.name) -- default

rawget / rawset 绕过元方法:

rawget(t, "name")
rawset(t, "name", "Tom")

面向对象

table + function + metatable
Object = {}

function Object:new()
    local obj = {}
    setmetatable(obj, self)
    self.__index = self
    return obj
end

: 调用自动传 self

obj:Move()

等价于 obj.Move(obj)。子类调父类方法常写 self.base.Move(self),别把类表当 self

自带库

常用内容 说明
os os.timeos.date 时间戳、日期表
math absfloorceilrandomsqrt 数学计算
string findsubgsubformat 字符串处理
table insertremovesortconcat 表处理
package package.pathpackage.loaded 模块搜索路径和加载缓存
debug debug.traceback 调试和错误堆栈
print(os.time())
local now = os.date("*t")
print(now.year, now.month, now.day)
math.randomseed(os.time())
print(math.random(100))
print(package.path)

debug.traceback 看错误堆栈。

垃圾回收

collectgarbage("count")
collectgarbage("collect")
方法 作用
collectgarbage("count") 获取当前 Lua 内存占用,单位 KB
collectgarbage("collect") 手动触发一次完整 GC

GC 看可达性,不是引用计数:

local a = {}
local b = {}

a.other = b
b.other = a

a = nil
b = nil

外部访问不到后仍可回收。弱表、__gc、增量/分代 GC 可以后面再仔细学习。热路径别频繁手动 collect

错误处理

方法 作用
error 主动抛出错误
assert 条件不成立时抛错
pcall 保护调用,接住错误
xpcall 带错误处理函数的保护调用
debug.traceback 生成错误堆栈
error("这里主动抛出一个错误")
assert(playerId ~= nil, "playerId不能为空")
local ok, result = pcall(function()
    error("出错了")
end)

print(ok)
print(result)
local ok, err = xpcall(function()
    error("出错了")
end, debug.traceback)

print(ok)
print(err)

事件分发、热更入口、UI/网络回调等位置适合统一错误保护。

Lua版本差异

print(_VERSION)

if jit then
    print(jit.version)
end

_VERSION 在 LuaJIT 下也可能显示 Lua 5.1,判断 LuaJIT 要看 jit.version

版本 常见差异点 项目里怎么记
LuaJIT Lua 5.1 语义为主,内置 bit,带部分 Lua 5.2 / 5.3 扩展 不要当成完整 Lua 5.3 / Lua 5.4
Lua 5.1 unpacksetfenv/getfenvmodule 老项目、Unity Lua 热更、LuaJIT 体系常见
Lua 5.2 _ENVgoto__pairs / __ipairs 环境机制从 setfenv 转向 _ENV
Lua 5.3 整数 / 浮点、原生位运算、table.moveutf8 新语法不要直接搬进 Lua 5.1 项目
Lua 5.4 incremental / generational GC、<close>__closecoroutine.close 资源关闭和协程关闭语义要注意

跨版本通用坑:

  • # 对有洞 table 的结果不要依赖。
  • pairs 遍历顺序不要依赖。
  • ipairs 遇到中间 nil 通常会停。
  • table.insert(t, pos, value) 中间插入会移动后面的元素。

最终以项目嵌入的运行时为准。


20.2 面试题精选

基础题

1. Lua 里哪些值会被当成 false?

题目

Lua 的真值规则是什么?0、空字符串 ""、空表 {} 分别算真还是假?

深入解析
  • Lua 只有 nilfalse 会被当成“假”,其余一律为“真”。
  • 0、空字符串、空表在 Lua 里都是真。
  • 会影响 if 判断、and/or 的返回值逻辑,以及模拟三目写法。
答题示例

Lua 里只有 nilfalse 是假,其它都是真。
0、空字符串、空表都是真。实际写条件判断时,不能用 if x then 来判断“是否为 0 / 空字符串 / 空表”,要写成更明确的比较条件。

参考文章
  • 4.简单变量类型
  • 7.条件分支语句
  • 12.特殊语法

2. and / or 的短路和返回值规则是什么?

题目

Lua 的 and / or 除了短路,还有什么“非布尔返回值”特性?a and b / a or b 分别返回什么?

深入解析
  • and:左操作数为假,返回左值;左操作数为真,返回右值。
  • or:左操作数为真,返回左值;左操作数为假,返回右值。
  • 它们返回的是参与运算的原始值,不一定是 true / false
  • x = x or default 很常见,但如果 false 是合法值,就不能这么写。
答题示例

and 是“左假返回左,左真返回右”;or 是“左真返回左,左假返回右”,并且都会短路。
所以 Lua 里经常用 x = x or default 做默认值,但如果 false 是合法值,就要显式判断 nil,不能直接用 or 兜底。

参考文章
  • 6.运算符
  • 12.特殊语法

3. .. 拼接字符串有哪些坑?怎么写更好?

题目

.. 拼接非字符串时会怎样?遇到 nil 会怎样?如何避免拼接时报错?

深入解析
  • .. 用来做字符串拼接。
  • number 通常可以转成字符串参与拼接。
  • nil 不能直接拼接,会报错。
  • 日志拼接里,变量可能为空时最好先 tostring
答题示例

.. 是字符串拼接。number 通常可以参与拼接,但 nil 会报错。
项目里写日志时,我一般会用 tostring 包一下,比如 "playerId = " .. tostring(playerId),避免某个值为空时日志代码自己先炸掉。

参考文章
  • 5.字符串操作
  • 9.函数

4. Lua 函数参数个数不匹配会怎样?

题目

Lua 调函数时,多传参数、少传参数会不会报错?

深入解析
  • Lua 调函数时参数个数比较宽松。
  • 少传的参数会补 nil
  • 多传的参数会被丢弃。
  • 关键函数的参数是否合法,需要自己写检查。
答题示例

Lua 调函数时,少传参数会补 nil,多传参数会丢弃,不会像 C# 那样直接编译期报错。
所以项目里的关键函数一般要自己做参数检查,比如用 assert 或普通 if 判断。

参考文章
  • 9.函数
  • 18.错误处理

进阶题

1. #t 为什么“不可靠”?什么时候能用?

题目

Lua 里 #table 的长度规则是什么?为什么带 nil 的数组 # 结果可能不稳定?项目里该怎么处理?

深入解析
  • #t 适合连续数组,也就是从 1 开始,中间没有 nil
  • 一旦中间出现 nil,就变成有洞 table,#t 的结果不要依赖。
  • 字典、稀疏数组、混合表,不要用 # 当数量。
  • 真要统计数量,可以自己维护 count,或者按业务规则遍历统计。
答题示例

#t 只适合连续数组。只要中间有 nil,结果就不要依赖,不同版本、不同构造方式下都可能和直觉不一致。
连续数组可以用 #,字典和稀疏表不要用 # 当数量,项目里好的方式是自己维护 count 或按规则遍历统计。

参考文章
  • 10.表
  • 19.Lua版本差异

2. pairsipairs 的区别是什么?

题目

pairsipairs 分别适合遍历什么表?各自有什么坑?

深入解析
  • ipairs 一般用于连续数组,从 1 开始往后遍历,遇到第一个 nil 通常会停。
  • pairs 可以遍历表里的所有键值对,但遍历顺序不要依赖。
  • 需要固定顺序时,不要依赖 pairs,自己维护 key 列表。
答题示例

ipairs 适合连续数组,从 1 开始遍历,遇到 nil 通常会停。
pairs 适合遍历所有键值对,包括字符串 key、数字 key,但顺序不保证。
如果业务需要固定顺序,就自己维护 key 列表。

参考文章
  • 10.表
  • 19.Lua版本差异

3. require 的缓存机制是什么?怎么强制重新加载?

题目

require 会不会重复执行脚本?它的缓存在哪里?怎么卸载并重新加载一个模块?

深入解析
  • require 首次加载会执行目标脚本。
  • 脚本返回值会缓存到 package.loaded[name]
  • 后续 require(name) 直接返回缓存,不会重复执行。
  • 强制重载可以先把 package.loaded[name] 置空,再重新 require
答题示例

require 默认只执行一次,结果会缓存在 package.loaded
要重载就把 package.loaded["xxx"] = nil,然后再 require("xxx") 触发重新执行。这个适合测试或工具场景,正式业务里不要随便清模块缓存。

参考文章
  • 11.模块与多脚本

4. setmetatable / __index / rawget 分别解决什么问题?

题目

Lua 元表的 __index 查找链是什么?rawget 为什么能绕过元表?常见用法有哪些?

深入解析
  • setmetatable(t, mt) 给表设置元表。
  • 当访问 t[k] 找不到键时,会看元表的 __index
  • __index 可以是一张表,也可以是一个函数。
  • rawget(t, k) 只查 t 自己,不触发 __index
答题示例

__index 是“缺键时的兜底查找”:可以是表,也可以是函数。
rawget 只查原表本身,不走元表链,常用于绕过代理表、调试元表行为,或者判断字段到底是不是表自己持有的。

参考文章
  • 14.元表

5. pcallxpcall 怎么选?

题目

Lua 里 pcallxpcall 都能保护调用,它们有什么区别?项目里什么时候用 xpcall

深入解析
  • pcall 可以保护一次函数调用,报错时不会直接中断外层流程。
  • pcall 返回是否成功,以及结果或错误信息。
  • xpcall 可以指定错误处理函数,常见搭配是 debug.traceback
  • 框架入口、事件派发、脚本回调这些位置更适合用 xpcall 打完整堆栈。
答题示例

pcall 适合简单保护调用,能拿到是否成功和错误信息。
xpcall 可以指定错误处理函数,所以项目里经常配合 debug.traceback 打完整堆栈。
普通局部保护用 pcall,框架入口和回调分发更适合 xpcall

参考文章
  • 18.错误处理

深度题

1. 用 Lua 实现“类 / 继承 / 多态”时,:. 的差异会引发什么 bug?

题目

Lua 里 obj:func()obj.func(obj) 等价吗?在调用父类方法时为什么有时必须用 . 并显式传 self

深入解析
  • : 调用会把调用者作为第一个参数,也就是 self,隐式传入。
  • obj:func(x) 等价于 obj.func(obj, x)
  • 子类调用父类方法时,如果传错 self,可能把类表当成实例,导致字段写到类表上。
  • 常见写法是 self.base.Move(self)
答题示例

: 本质是帮忙隐式传 self
obj:func(x) 等价于 obj.func(obj, x)
子类调用父类方法时,要保证传进去的是实例,不然容易把类表当 self,改到共享字段上。常见写法是 self.base.Move(self)

参考文章
  • 15.面向对象

2. Lua GC 回收的是什么?是不是引用计数?

题目

Lua GC 是不是简单的引用计数?两个表互相引用会不会永远回收不了?

深入解析
  • Lua GC 主要看对象是否可达,不是简单引用计数。
  • 对象如果还能从全局变量、局部变量、调用栈、表字段等地方访问到,就可以先理解为可达。
  • 对象如果已经没有地方能访问到,就可以先理解为不可达,后续可能被 GC 回收。
  • 两个表互相引用,不代表一定泄漏。只要整个引用环从外部不可达,GC 仍然可以回收。
答题示例

Lua GC 不是简单引用计数。基础理解是:对象如果从外部已经访问不到了,就可以认为不可达,后续可能被 GC 回收。
两个表互相引用本身不代表一定泄漏,只要整个引用环从外部不可达,GC 仍然能回收。

参考文章
  • 17.垃圾回收

3. LuaJIT 和 Lua 5.3 / 5.4 是什么关系?

题目

LuaJIT 是不是 Lua 的最新版?为什么 Unity Lua 热更项目里不能直接写 Lua 5.3 / Lua 5.4 的新语法?

深入解析
  • LuaJIT 不是 Lua 官方主线的 5.3 / 5.4。
  • LuaJIT 整体更接近 Lua 5.1。
  • LuaJIT 内置 bit,也带部分 Lua 5.2 / 5.3 扩展。
  • 但 LuaJIT 不是完整 Lua 5.3 / Lua 5.4。
  • Unity Lua 热更项目里,最终以项目实际嵌入的 Lua 运行时为准。
答题示例

LuaJIT 不是 Lua 最新版,它主要按 Lua 5.1 语义来跑,同时做了 JIT 优化,内置 bit,也带部分扩展。
但不能把它当完整 Lua 5.3 / 5.4 用。比如 Lua 5.3 的原生位运算符、Lua 5.4 的 <close>,在 LuaJIT 项目里都不要默认可用。实际写热更代码时要先确认项目运行时。

参考文章
  • 2.开发环境搭建
  • 19.Lua版本差异

4. Lua 5.1 的 setfenv 和 Lua 5.2 的 _ENV 有什么关系?

题目

Lua 5.1 里常见的 setfenv / getfenv 和 Lua 5.2 之后的 _ENV 分别解决什么问题?

深入解析
  • Lua 5.1 常见 setfenv / getfenv,用来处理函数环境。
  • 可以简单理解成:函数查全局变量时,用哪张表去查。
  • Lua 5.2 之后,环境机制更多基于 _ENV 理解。
  • local、非 upvalue 的名字,会通过当前可见的 _ENV 去查。
  • 这类机制常用于沙盒、配置脚本、限制全局变量访问。
答题示例

Lua 5.1 常用 setfenv / getfenv 控制函数环境,换掉函数查全局变量时用的那张表。
Lua 5.2 之后更多通过 _ENV 理解环境,非 local 的变量会走当前 _ENV 查找。
项目里这类机制多用于沙盒和配置加载,普通业务代码里不建议随便改环境。

参考文章
  • 11.模块与多脚本
  • 19.Lua版本差异

5. Lua 5.4 的 __close__gc 有什么区别?

题目

Lua 5.4 的 to-be-closed 变量、__close 和传统 __gc 有什么区别?为什么还要有 coroutine.close

深入解析
  • local x <close> = value 表示变量离开作用域时需要关闭。
  • __close 配合 <close> 使用,偏及时释放资源。
  • __gc 偏 GC 阶段收尾,触发时机由 GC 决定。
  • coroutine.close 和挂起协程里的 to-be-closed 变量有关。
  • 协程 yield 后如果不再恢复,可以用 coroutine.close 主动关闭协程,并处理里面还没关闭的 to-be-closed 变量。
答题示例

__close 更像作用域结束时的资源关闭,适合文件句柄、连接、C 层资源这类需要及时释放的东西。
__gc 是 GC 阶段的收尾,触发时机不确定。
coroutine.close 主要是为协程场景补关闭语义:协程 yield 后如果不再恢复,可以主动 close,让里面挂着的 to-be-closed 变量走关闭逻辑。

参考文章
  • 13.协同程序
  • 14.元表
  • 17.垃圾回收
  • 19.Lua版本差异


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏