【问题标题】:Lua userdata being garbage collected before it should beLua 用户数据在它应该被垃圾收集之前
【发布时间】:2014-06-04 04:43:12
【问题描述】:

我是 Lua 的新手。我正在尝试使用 Cocos2d-x v3.1rc0 创建游戏。

我遇到了一个问题,其中一个由 Cocos2d-x 库创建的对象似乎在应该被垃圾收集之前被垃圾收集了。

这是一个跟踪屏幕上“猎物”的类。每个猎物都会引用将根据猎物状态显示的帧/动画。

Prey = {
    sprite = false -- Main prey sprite.
    -- Frames used for movement, getting hit, knocked out, etc.
  , frame = {
        idle = false -- idle frames
      , move = false -- move frames
      , rest = false -- resting frames
    }
  , state = false -- The current state of the Prey.
}

这里是构造函数:

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

这是帧与猎物相关联的位置:

function Prey:setFrames(frames)
    --[[ Moving ]]--
    self.frame.move = cc.Animation:createWithSpriteFrames({frames[1], frames[2], frames[3]}, 1.0)
    cclog("move frames: " .. #self.frame.move:getFrames())

    --[[ Resting ]]--
    self.frame.rest = cc.Animation:createWithSpriteFrames({frames[4], frames[5]}, 2.0)
    cclog("rest frames: " .. #self.frame.rest:getFrames())
end

上面将打印以下内容:

cocos2d: [LUA-print] move frames: 3
cocos2d: [LUA-print] rest frames: 2

但是,当我尝试调用以下方法时,frame.move 和 frame.rest 变量似乎已被垃圾回收,因为当我尝试访问它们时会引发错误。请注意,此方法在每个刻度上都会调用:

function Prey:tick()
    cclog("state: " .. self.state)

    local animation = false

    -- Moving
    if (self.state == PreyState.MOVING)
    then
        cclog("moving: " .. #self.frame.move:getFrames())
        animation = self.frame.move
    elseif (self.state == PreyState.RESTING)
    then
        cclog("resting: " .. #self.frame.rest:getFrames())
        -- Resting
        animation = self.frame.rest
    end
end

当针对这两种情况之一进行 cclog 调用时,将显示以下错误。请注意,我知道 Prey 的这个特定实例没有被垃圾回收,因为在我调用 tick 方法之前 self.state 被设置为空闲。它还在对该方法的后续调用中保留 self.state。

cocos2d: [LUA-print] state: 2
cocos2d: [LUA-print] ----------------------------------------
cocos2d: [LUA-print] LUA ERROR: [string "Prey.lua"]:189: invalid 'cobj' in function  'lua_cocos2dx_Animation_getFrames'

在查看了许多描述如何保留对象的文章之后,似乎我的 Animation 对象可能正在被垃圾收集。但我不知道为什么!对 Cocos2d-x 对象的引用应该很强吧?

以下是我读过的一些关于该主题的文章:

更新 1

以下代码是导致问题的原因:

-- Create the instance of our prey object here...
prey = new Prey:new()
local function tick()
    prey:tick()
end
scheduleID = cc.Director:getInstance():getScheduler():scheduleScriptFunc(tick, 0, false)

但是,当我尝试在调度程序之外简单地调用 prey:tick() 时,我没有收到任何错误。我需要在每一次滴答声中运行这段代码......我错过了什么?在这个特定的场景中,我将prey 设为全局变量,以便 tick 方法可以访问 Prey 的唯一实例。我这样做是为了简化这个特定的测试。但是,我想让 prey 本地化并让调度程序为 Prey 的每个实例运行 tick 函数。

【问题讨论】:

  • 好吧,这似乎不是表格的问题。当我将 Cocos2d-x 对象的引用移动到不属于该类的全局变量中时,它仍然会产生该错误。
  • 我只能在创建它的块的上下文中引用一个 Cocos 对象吗?这看起来真的很傻。

标签: lua garbage-collection cocos2d-x cocos2d-x-3.0


【解决方案1】:

对象正在被垃圾回收。我又进行了几次测试,得出的结论是,您不能将任何 Cocos2d 对象关联到变量,除非它们稍后在下一个周期被垃圾收集。相反,您必须使用 Cocos2d-x 的库来缓存您的纹理,然后在需要时查询这些纹理。

很难说明,但最终结果是在我需要纹理时调用cc.Director:getInstance():getTextureCache():getTextureForKey("texture.png"),并在对象的状态发生变化时重新创建每个动画所需的帧。我将寻找一种缓存精灵的方法,这样我就不必重新创建它们了。但是,这解决了我的问题。所以教训是 Cococ2d 对象在下一个周期被垃圾收集。唯一保留的对象是 Cocos2d 内部缓存的对象。我可能错了,但这是我观察到的。

所以我的流程是在加载时先将图片添加到纹理缓存中:

local texture = cc.Director:getInstance():getTextureCache():addImage("texture.png")

然后稍后调用以下内容:

local texture = cc.Director:getInstance():getTextureCache():getTextureForKey("texture.png")

我非常感谢对此的任何见解。你们是怎么处理这件事的?这是正确的过程吗?是否有任何与缓存精灵、纹理等相关的文章供以后使用?谢谢!

更新 1

是的。对象正在从 Cocos2d-x 库中释放。您可以通过缓存精灵帧来解决这个问题。这是我在 Lua 中用来缓存精灵帧的代码。缓存后,您在 Lua 代码中对 SpriteFrame 的任何引用都将继续指向该帧的活动实例。

cc.SpriteFrameCache:getInstance():addSpriteFrame(frame, name)

然后将 SpriteFrame 从缓存中取回:

local frame = cc.SpriteFrameCache:getInstance():getSpriteFrame(name)

【讨论】:

  • 其实有一种方法:保持一个对它们的引用,它们不会被垃圾回收。如果他们不使用 gc-meta 引用任何 lua 对象,添加一个 gc-meta 并在终结器中读取终结器队列就足够了...
  • 将表键分配给值应该足够了吧?我相信我的问题是我错误地创建了 Prey 对象。稍后我将尝试使用我创建课程的方式发布更新。
  • 我再次测试了它,但这次是更新的类。我不知道为什么,这可能只是我对一切如何运作的误解,但它仍然有同样的问题。一旦下一帧尝试访问该成员 var,它就会抛出错误。哦,好吧!
猜你喜欢
  • 1970-01-01
  • 2012-01-19
  • 2021-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-25
  • 2010-12-15
相关资源
最近更新 更多