复制函数变量会产生意外的结果。

我有一个使用以下函数创建的对象

local function newObject(functionVariable)
    ...
    functionVariable = functionVariable or nop
    ...
    return setmetatable({
        ...
        functionVariable         = functionVariable,
        ...
    }, Objectmt)
end

当我使用下面的函数来深拷贝这个对象

local function deepcopy(t)
    if t == nil then return nil end

    if type(t) == 'table' then
            local copy = {}

            for k, v in pairs(t) do
                    copy[k] = deepcopy(v)
            end
            setmetatable(copy, deepcopy(getmetatable(t)))
            return copy
    else -- number, string, boolean, etc
            return t
    end
end

并使用下面的代码加载对象

for k, v in pairs(state.objectsTable) do objectsTable[k] = v end

函数变量完全是错的。它不再是传入对象中的函数,并产生意外的结果。

点赞
用户4117435
用户4117435

#深拷贝可能存在问题

有很多事情可能出错,但不足以明确。然而,我可以看到你的副本中可能出错的一些事情,其中一个问题特别严重。

你的deepcopy函数将会拷贝对象的metatable :

-- 省略 ...
setmetatable(copy, deepcopy(getmetatable(t)))
-- ...

然而,对于相同类型的对象共享同一metatable是非常常见的做法,而且,你的代码似乎也这样做了(尽管没有看到Objectmt的定义,不太清楚)。其他代码可以通过以下方式确定某个对象是否为对象:

function isObject(obj)
  return getmetatable(obj)==Objectmt
end

这会导致你的拷贝失败,因为metatable不再相同(即使它们内容相同)。

这可以通过使用一个不同版本的deepcopy(或修改现有的版本)以重用metatable来修复:

-- 省略 ...
setmetatable(copy, getmetatable(t))
-- ...

如果这不是问题,那么需要考虑以下一些事情:

  • 你的deepcopy函数不会拷贝表键。在某些情况下,拷贝键可能很重要,但对于你展示的任何代码都不是这样。
  • 你的deepcopy函数将不会复制函数。如果你在使用可变upvalue函数,那么这可能很重要。同样,这也不是你展示的任何代码的情况。
  • 可能还有其他表(除metatable之外)应该通过引用而不是值进行拷贝。要知道哪个是适当的,必须理解它们的使用方式。
  • 可能存在userdata或其他不太常见的类型(例如协程)被目前以引用方式拷贝,需要以值方式进行拷贝。一些东西可能无法以值的方式进行拷贝。
  • 你要拷贝的数据中可能有一些不应该拷贝的东西,例如唯一标识符。
  • 问题可能不在拷贝中。
2015-03-22 08:18:20