在 Lua 中将表及其子表设置为 nil 有啥区别?

Posted

技术标签:

【中文标题】在 Lua 中将表及其子表设置为 nil 有啥区别?【英文标题】:What is the difference with tables and their subtables in setting them to nil in Lua?在 Lua 中将表及其子表设置为 nil 有什么区别? 【发布时间】:2019-08-17 23:56:18 【问题描述】:

我知道 Lua 会在内存中保留一个表,只要在代码中的某处仍然存在对它的引用,这意味着您不能通过将表作为参数传递给函数来将它们设置为 nil。但是,我不明白为什么您可以对子表执行此操作。

local a = 
a.b = 

local function remove( t )
    t.b = nil
    t = nil
end

remove( a )

print( a and a.b, a )

在将 a 作为参数传递给函数 remove 之后,我将参数及其子表设置为 nil。由于上述原因,我知道这不应该将 a 设置为 nil,但是有人可以告诉我为什么 a.b 可以设置为 nil,即使我的理解,它仍然应该从外部引用它,就像 a 一样。

【问题讨论】:

【参考方案1】:

我知道 Lua 会在内存中保留一个表,只要在代码中的某处仍然存在对它的引用,

是的,但这实际上意味着我们不必担心内存管理。我们通常不会考虑何时对表进行垃圾收集,除非我们的内存非常紧张并且必须调整垃圾收集器。对于正常使用,我们可以假设一旦我们失去对它的最后一次引用,表就会从内存中删除。

这意味着您不能通过将表作为参数传递给函数来将它们设置为 nil。

这并不是真正的原因。表和 nil 是两种不同的数据类型,因此您不能从字面上将表设置为 nil。通常我们所说的“将表设置为 nil”的意思是获取引用表的变量或表键,并将该变量/键设置为 nil。这可能是也可能不是我们对该表的最后一个引用。

函数不能设置参数变量的原因是参数(函数的局部变量)和用作参数的变量是两个完全不同的变量。

但是有人可以告诉我为什么 a.b 可以设置为 nil,尽管根据我的理解,它仍然应该从外部引用它,就像使用 a 一样。

不,表a.b 没有外部引用。没有引用该表的局部变量。表键没有变量的作用域。对该表的唯一引用是在表 a 内部,它被语句 t.b = nil 修改。

t.b = nil 语句有效,因为它修改了 t,不止一个变量引用该表。 't = nil' 分配给 变量 t,它基本上与任何其他变量无关。

【讨论】:

很好的解释。 “用作参数的变量”:是的,参数就是表达式。作为变量的表达式是一种特殊情况,但一点也不特殊。调用者被传递了表达式的值。它不会知道价值的来源。【参考方案2】:

严格来说,您不是“将a.b 设置为nil”。描述t.bil = nil 所做工作的准确方式是

“将值nil与表a中的键"b"相关联”

例如,将您写的内容与以下内容进行比较:

local o = 
o[1] = "one"
o[2] = "two"
o[3] = "three"

-- o currently looks like
--  KEY | VALUE
-- -----+--------
--    1 | "one"
--    2 | "two"
--    3 | "three"


o[2] = nil

-- o now looks like
--  KEY | VALUE
-- -----+--------
--    1 | "one"
--    3 | "three"

这很明显不是“将数字2 设置为零”。这只是修改表o内容,不再将键2 与任何值关联。

回顾一下你最初询问的代码:

local alpha = 
local beta = 

alpha[1] = "one"
alpha[2] = beta
alpha[3] = "three"

-- alpha now looks like
--  KEY | VALUE
-- -----+--------
--    1 | "one"
--    2 | <beta>
--    3 | "three"

alpha[2] = nil

-- alpha now looks like
--  KEY | VALUE
-- -----+--------
--    1 | "one"
--    3 | "three"

同样,beta 不会以任何方式被修改,数字2 也不会被修改。只是,2 与值beta 的关联已从表alpha 的内容中删除。

【讨论】:

在您的代码中,local beta = ; alpha[2] = beta; alpha[2] = nil; 这会将对 beta 的引用设置为 nil,但实际上并未将 beta(表)设置为 nil。我明白了。但是,在我的代码中,由于子表 a.b 被分配了 nil 值并且不再可访问,那么它与 nil 不一样并且不会被垃圾收集吗?或者它会保留为表的一部分,但以不可访问的方式,直到它所属的表最终正确设置为 nil? 你的代码就是这样的。 .b 就像 [2] a.b 在您的初始代码中不是表的一些特殊“家”。隐含地创建了一个“局部”变量来保存,然后将该局部变量放入a 中的键b 中,如图所示。

以上是关于在 Lua 中将表及其子表设置为 nil 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

lua 中pairs 和 ipairs区别

Lua表(table)的个人总结

为什么lua要判断空值

在 Angular 中将常量作为道具传递有啥区别? [复制]

lua pairs和ipairs的区别

self.foo=nil 和 [self setFoo:nil] 有啥区别?