Best practice for removing objects created in a module (pseudo OOP)

Hi all,

I am just wondering what is the best practice for removing objects, I want to make sure that I have not unintentionally made memory leak.  My question is best illustrated in an example.

If I have a module I use to create characters in a game and I add a bunch of methods to this object.

[lua]

local public = {}

public.new = function()

  local g = display.newGroup()

  local character = display.newCircle(g, 100,100,100)

  function g:destroy () – called to destroy character,  could be called as part of collision or custom event

    display.remove(self)

  end

  function g:finalize ()

    – clean up timers and runtimes

    print(“removing g”)

    self = nil – is this needed?

  end

  g:addEventListener(“finalize”)

  return g

end

return public

[/lua]

And I run the following code in my main.lua

[lua]

local t = require (“test”)

local obj = t.new()

print(obj) – prints “table: 0x7fee98f89780”

obj:destroy()

print(obj) – prints “table: 0x7fee98f89780”

[/lua]

Is this obj cleaned up properly?  Since obj still contains a reference to the table does this create a memory leak, or will would this be handled by scope if I changed scenes?

Do I need to “self = nil”  in the finalize event for the object?

Does it work the same in a table?

[lua]

local objects = {}

for i = 1, 5 do

  objects[i] = t.new()

end

objects[3]:destroy()

for i = 1, 5 do

  print(objects[i]) – prints table reference

end

[/lua]

Thanks in advance,

Craig

Any input on this? 

Hi.

An object in Lua may be garbage collected after it becomes unreachable, meaning nothing outside of Lua’s own code holds any references to it.

In the above, during the new () call, you have one reference to your group. That group in turn has some reference (probably indirect) to some state managed by Corona, e.g. position information, display hierarchy links, etc. Corona itself will also keep such a reference to this state. This is why you can do “fire and forget” groups, where you just make it, populate it, insert it, and never keep around a variable.

So now you have one reference to the group itself and two to some hidden Corona state.

(Of course this is a simplification. If you pass the result of new () to multiple variables and / or table fields, as many references are kept for their respective lifetimes. Similarly, the group’s parent–whether another group or the stage–will also hold a reference.)

When you remove all references to the group, it will go away and so will one of those references to internal details. But there’s still one reference left on the latter, so it remains even though you don’t have immediate access to it.

A display.remove() or removeSelf(), on the other hand, will scour both those internal references (and some related stuff like the metatable), while leaving the (ex-group) table around.

The “self = nil” thing operates on the same principle. The finalize method adds another reference to the group. This will indeed disappear as you go out of scope, so no, no need for that.

I hope at least some of that was clear. This is not always easy stuff to write.  :slight_smile:

Actually, in light of your “Since obj still contains a reference to the table” comment, I would add that there’s a distinction between variables and values.

A variable is something like g and self. A value would be your table itself.

A variable is a box that can hold a value or nil. When you assign to a variable, you’re taking whatever’s in the box out and putting something new in.

The assignment to self only changes what’s in that particular box. “No references” corresponds to a variable not being found in any box.

Ignoring your finalise which will never get called…

you should code as

 function g:destroy () -- called to destroy character, could be called as part of collision or custom event display.remove(self) self = nil end

Thanks StarCrunch and SGS.

Had to read it a few times, but I think I get it.

Cheers,

Craig

I think SGS’s point is:  if you’re going to explicitly call destroy(), as you do, then there’s no need for finalize().

finalize is like an “automatic” way to catch that same moment of destruction IF you allow “random code” to destroy that group - fe if you had “display.remove(obj)” in main.lua instead of “obj:destroy()”

having both isn’t necessarily bad, but it’s redundant - the display.remove() in destroy() will trigger the finalize() - so you could just put it all in destroy() and simplify

i myself prefer explicit “destroy()”'s rather than relying on finalize events, as finalize had a notorious history of not always firing (since fixed, and does fire now, but my habit remains)

the reference to “obj” will continue to exist in the local scope of main no matter what you do in destroy() and/or finalize()

in the local scope of main you need to do “obj = nil” to release that last reference before it’ll be garbage collected

Any input on this? 

Hi.

An object in Lua may be garbage collected after it becomes unreachable, meaning nothing outside of Lua’s own code holds any references to it.

In the above, during the new () call, you have one reference to your group. That group in turn has some reference (probably indirect) to some state managed by Corona, e.g. position information, display hierarchy links, etc. Corona itself will also keep such a reference to this state. This is why you can do “fire and forget” groups, where you just make it, populate it, insert it, and never keep around a variable.

So now you have one reference to the group itself and two to some hidden Corona state.

(Of course this is a simplification. If you pass the result of new () to multiple variables and / or table fields, as many references are kept for their respective lifetimes. Similarly, the group’s parent–whether another group or the stage–will also hold a reference.)

When you remove all references to the group, it will go away and so will one of those references to internal details. But there’s still one reference left on the latter, so it remains even though you don’t have immediate access to it.

A display.remove() or removeSelf(), on the other hand, will scour both those internal references (and some related stuff like the metatable), while leaving the (ex-group) table around.

The “self = nil” thing operates on the same principle. The finalize method adds another reference to the group. This will indeed disappear as you go out of scope, so no, no need for that.

I hope at least some of that was clear. This is not always easy stuff to write.  :slight_smile:

Actually, in light of your “Since obj still contains a reference to the table” comment, I would add that there’s a distinction between variables and values.

A variable is something like g and self. A value would be your table itself.

A variable is a box that can hold a value or nil. When you assign to a variable, you’re taking whatever’s in the box out and putting something new in.

The assignment to self only changes what’s in that particular box. “No references” corresponds to a variable not being found in any box.

Ignoring your finalise which will never get called…

you should code as

 function g:destroy () -- called to destroy character, could be called as part of collision or custom event display.remove(self) self = nil end

Thanks StarCrunch and SGS.

Had to read it a few times, but I think I get it.

Cheers,

Craig

I think SGS’s point is:  if you’re going to explicitly call destroy(), as you do, then there’s no need for finalize().

finalize is like an “automatic” way to catch that same moment of destruction IF you allow “random code” to destroy that group - fe if you had “display.remove(obj)” in main.lua instead of “obj:destroy()”

having both isn’t necessarily bad, but it’s redundant - the display.remove() in destroy() will trigger the finalize() - so you could just put it all in destroy() and simplify

i myself prefer explicit “destroy()”'s rather than relying on finalize events, as finalize had a notorious history of not always firing (since fixed, and does fire now, but my habit remains)

the reference to “obj” will continue to exist in the local scope of main no matter what you do in destroy() and/or finalize()

in the local scope of main you need to do “obj = nil” to release that last reference before it’ll be garbage collected