Remove all objects

Hello-

I have code in my app that spawns a piece multiple times. Currently, I have that every time a user clicks on a button, this is called

T\_supporter = display.newImage("T\_supporter.png")

I was trying to set up a way to remove them all by calling this

T\_supporter:removeSelf()

but it would only remove the object that was created on the first spawn and none after that.

Can you please show me where I went wrong? [import]uid: 35210 topic_id: 7489 reply_id: 307489[/import]

Try creating a group, then add every object you spawn into that group. When you want to remove everything just remove the group [import]uid: 14018 topic_id: 7489 reply_id: 26507[/import]

I have tried creating a group and every time a spawn happens, I call this

blocksGroup:insert(T\_supporter [false])

then, when all is said and done, I call this to get rid of all of them

 blocksGroup:removeSelf() 

What am I doing wrong? [import]uid: 35210 topic_id: 7489 reply_id: 26525[/import]

What’s that false thing you got there? [import]uid: 14018 topic_id: 7489 reply_id: 26535[/import]

Unfortunately, you can’t just remove the group and expect all the objects within it to be removed as well.

Here’s a function you can use (taken from Ricardo Rauber’s director class):

[blockcode]
local function cleanGroups ( curGroup, level )
if curGroup.numChildren then
while curGroup.numChildren > 0 do
cleanGroups ( curGroup[curGroup.numChildren], level+1 )
end
if level > 0 then
curGroup:removeSelf()
end
else
curGroup:removeSelf()
curGroup = nil
return
end
end
[/blockcode]

Add that function to the top of your lua file, and then you’d call:

cleanGroups( blockGroups, 0 )

For extra assurance, you can even do a:

blockGroups = nil

After you call cleanGroups() just to be on the safe side. What that will do is first remove all objects/groups within blocksGroup, and then it’ll remove blocksGroup itself.

Hope that helps! [import]uid: 7849 topic_id: 7489 reply_id: 26540[/import]

Not sure if I understand how this function works…

As I understand it, it only will recursively removeSelf any display groups and not any display objects that are inside of a group as for a display object o, o.numChildren returns nil.

Furthermore, the reason for the “level” parameter seems to be to do a “curGroup = nil” when you’re down in a recursion. But as “curGroup” is a function parameter, it will cease to exist outside of the function scope and nil’ing the reference should have no effect.

Rewriting it to truly removeSelf all display object and groups, and not to bother to nil any parameters that will “self-destruct” outside the function scope, the following could be an alternative:

[lua]— Function will bottom-up recursively removeSelf all display objects and groups that may be part of objectOrGroup, before removeSelf’ing itself.
@param objectOrGroup a display object or group to be removeSelf’ed with all its possible members.
local function cleanGroups ( objectOrGroup )
if objectOrGroup.numChildren then
– we have a group, so first clean that out
while objectOrGroup.numChildren > 0 do
– clean out the last member of the group (work from the top down!)
cleanGroups ( objectOrGroup[objectOrGroup.numChildren])
end
end
– we have either an empty group or a normal display object - remove it
objectOrGroup:removeSelf()
return
end
end[/lua]

Please let me know if I misunderstood the working of this cleanGroups function.

I do agree that it is good to nil the reference to the objectOrGroup in the calling program, as there will be a table-skeleton or display-object-orphan left after the removeSelf, and holding on to references will impede the garbage collection of this orphan (unlike the function parameter). So maybe do a standard:
[lua]blockGroups = cleanGroups( blockGroups )[/lua]

-FrankS

[import]uid: 8093 topic_id: 7489 reply_id: 26557[/import]

@FrankS: I can’t see what you did besides rename curGroup to objectOrGroup and remove the level parameter…

As it stands, curGroup and objectOrGroup works the same, regardless of what it’s named. I have the function working in multiple apps and it does in fact remove all display objects and groups with the top-level group that you specify.

I do like your suggestion to simply assign the group to the function, so that it nils out on its own, but it’s all a matter of personal preference. Do it that way or just nil out after. What’s important is that it’s done :slight_smile: [import]uid: 7849 topic_id: 7489 reply_id: 26560[/import]

Sorry Jon, but I did move the “removeSelf” outside of the “if curGroup.numChildren” test, which makes all the difference for normal display objects.

I tried to annotate the original cleanGroups() to show the issue:

[lua]local function cleanGroups ( curGroup, level )
if curGroup.numChildren then
– you only get here if curGroup.numChildren ~= nil
– local o = display.newCircle(100,100,10)
– print(o.numChildren) => nil
– so display objects do not get here, only groups
while curGroup.numChildren > 0 do
cleanGroups ( curGroup[curGroup.numChildren], level+1 )
end
if level > 0 then
curGroup:removeSelf()
end
else
curGroup:removeSelf()
curGroup = nil
return
end – end of “if curGroup.numChildren”
– display objects fall thru here and do not get removeSelf’ed
end[/lua]

Now… the fact that it seems to work for you could imply that either you didn’t notice it or that display objects get automagically removed… or that it’s very late here and I suffered from a major brain fart, but then I’d like to know so please bear with me :wink:

-Frank.

[import]uid: 8093 topic_id: 7489 reply_id: 26568[/import]

Ah great point FrankS, will definitely take your function and do some testing…

Thanks! [import]uid: 7849 topic_id: 7489 reply_id: 26570[/import]

@FrankS: Before, it seemed as though display objects were in fact being removed, but I suppose there was some sort of reference left behind (and thus causing memory leaks, ouch!). Your updated cleanGroups() function seems to take care of that problem.

So thank you very much for that. The Director Class definitely needs the cleanGroups() function updated using this one. I always had a little instability problems while using the Director Class, and this might just be it! Thanks again…

For those who missed it, here’s the “good” cleanGroups function:

[blockcode]
local function cleanGroups ( objectOrGroup )
if objectOrGroup.numChildren then
– we have a group, so first clean that out
while objectOrGroup.numChildren > 0 do
– clean out the last member of the group (work from the top down!)
cleanGroups ( objectOrGroup[objectOrGroup.numChildren])
end
end

– we have either an empty group or a normal display object - remove it
objectOrGroup:removeSelf()

return
end
[/blockcode]

Thanks again @FrankS! [import]uid: 7849 topic_id: 7489 reply_id: 26574[/import]

Glad to hear that it makes a difference.

I find that all this display-object removeSelf stuff is difficult to get right as first you have to get a hold of each and every one of them, and second any additional reference to that removeSelf’ed display-object will keep the skeleton/orphan from being garbage collected.

Wonder if the Corona-runtime couldn’t help us more with at least removeSelf’ing some of these display objects automatically when it’s obvious that there is no other reference then the one inside of a display-object-group (?) or if there is simply no other reference. In the latter case, the rule would be that if the garbage collector would be able to remove it, then Corona should be able to removeSelf it…

Good night, FrankS.
[import]uid: 8093 topic_id: 7489 reply_id: 26577[/import]

Just one more issue before I forget: the current “cleanGroups ( objectOrGroup )” still chokes on objects that are not display objects or display-objects that have been removeSelf’ed already.

I use the following function to test whether an object is a valid display-object or group:

[lua]local coronaMetaTable = getmetatable(display.getCurrentStage())

— Returns whether aDisplayObject is a Corona display object.
– note that all Corona types seem to share the same metatable, which is used for the test.
@param aDisplayObject table - possible display object.
@return boolean - true if object is a display object
isDisplayObject = function(aDisplayObject)
return (type(aDisplayObject) == “table” and getmetatable(aDisplayObject) == coronaMetaTable)
end[/lua]

So by adding that test to cleanGroups(), you would make it more robust:
[lua]local function cleanGroups ( objectOrGroup )
if(not isDisplayObject(objectOrGroup)) then return end
if objectOrGroup.numChildren then
– we have a group, so first clean that out
while objectOrGroup.numChildren > 0 do
– clean out the last member of the group (work from the top down!)
cleanGroups ( objectOrGroup[objectOrGroup.numChildren])
end
end

– we have either an empty group or a normal display object - remove it
objectOrGroup:removeSelf()

return
end[/lua]

Now I’m really going to bed :wink:
-FrankS.
[import]uid: 8093 topic_id: 7489 reply_id: 26578[/import]

Well, I have tried to implement that code, but it gives me a stack overflow error [import]uid: 35210 topic_id: 7489 reply_id: 26592[/import]

It’s difficult to understand what caused the stack overflow without more details…

The attached snippet works for me as expected (save as main.lua and run):

[lua]local coronaMetaTable = getmetatable(display.getCurrentStage())

— Returns whether aDisplayObject is a Corona display object.
– note that all Corona types seem to share the same metatable, which is used for the test.
@param aDisplayObject table - possible display object.
@return boolean - true if object is a display object
local isDisplayObject = function(aDisplayObject)
return (type(aDisplayObject) == “table” and getmetatable(aDisplayObject) == coronaMetaTable)
end

local function cleanGroups ( objectOrGroup )
if(not isDisplayObject(objectOrGroup)) then return end
if objectOrGroup.numChildren then
– we have a group, so first clean that out
while objectOrGroup.numChildren > 0 do
– clean out the last member of the group (work from the top down!)
cleanGroups ( objectOrGroup[objectOrGroup.numChildren])
end
end

– we have either an empty group or a normal display object - remove it
objectOrGroup:removeSelf()

return
end

local o = display.newCircle(100,100,10)

local g = display.newGroup()

for i = 1,100 do
local lg = display.newGroup()
lg:insert(display.newCircle(math.random(200),math.random(300),math.random(100)))
g:insert(lg)
end

print(“before info”)
print(“o.numChildren”,o and o.name, o and o.numChildren, isDisplayObject(o))
print(“g.numChildren”,g and g.name, g and g.numChildren, isDisplayObject(g))

local info = function()
print(“before cleanGroups”)
print(“o.numChildren”,o and o.name, o and o.numChildren, isDisplayObject(o))
print(“g.numChildren”,g and g.name, g and g.numChildren, isDisplayObject(g))

cleanGroups ( o )
cleanGroups ( g )

print(“after cleanGroups”)
print(“o.numChildren”,o and o.name, o and o.numChildren, isDisplayObject(o))
print(“g.numChildren”,g and g.name, g and g.numChildren, isDisplayObject(g))
end

timer.performWithDelay(3000, info, 1)[/lua]

-FrankS.
[import]uid: 8093 topic_id: 7489 reply_id: 26613[/import]

@FrankS: Awesome, awesome stuff here.

I implemented the isDisplayObject and your cleanGroups function and it works flawlessly in my project. Thanks a lot for this.

If you don’t mind, I want to make a blog post about this. Don’t worry, will definitely give you all the credit as my source of this information, I just want it to be stored somewhere out there in the event this forum goes down (or this thread is buried so far that nobody will ever find it).

Thanks again FrankS, it was a big help.

@dellagd: You might try looking at other areas in your code, I don’t think that it’s directly having to do with your cleanGroups, it might just be something else in your code indirectly conflicting with it. [import]uid: 7849 topic_id: 7489 reply_id: 26616[/import]

Great News, got it all working

Just one thing, not really related to this whole ordeal very much, when I insert the objects into the group, they are sent behind my background image.
Can someone help me with this? [import]uid: 35210 topic_id: 7489 reply_id: 26632[/import]

Great to hear all that good news - I enjoy your blog - hopefully useful for others also - regards, FrankS. [import]uid: 8093 topic_id: 7489 reply_id: 26636[/import]

@dellagd: You can either do a myGroup:toFront() or… myBackground:toBack() [import]uid: 7849 topic_id: 7489 reply_id: 26679[/import]

My solution to this issue was essentially the inverse of the recursive one explained above. I wrapped the display.newGroup factory and used that to extend all subsequently created group objects with an enhanced removeSelf method which removes not only the group object itself but also its immediate children (which will in turn remove their children if they are also extended group objects). Here is an example showing how this effectively eliminates the memory leaks. What I particularly like about this solution is that it can be placed in a module of its own and simply required in, and with no other code changes your group-removal memory leaks just go away.

[lua]-- Fix memory leak in group:removeSelf() …
– … and proxy group:remove( o ) to o:removeSelf()
local oldNewGroup = display.newGroup
display.newGroup = function( … )
local group = oldNewGroup( … )
local oldRemoveSelf = group.removeSelf
group.removeSelf = function( self )
for i = self.numChildren, 1, -1 do
self[i]:removeSelf()
end
oldRemoveSelf( self )
end
group.remove = function( self, o )
if type( o ) == ‘number’ then
self[o]:removeSelf()
else
o:removeSelf()
end
end
return group
end
– patch stage object to proxy stage:remove( o ) to o:removeSelf()
display.getCurrentStage().remove = function( self, o )
if type( o ) == ‘number’ then
self[o]:removeSelf()
else
o:removeSelf()
end
end


Runtime:addEventListener(‘enterFrame’, function()
local g0 = display.newGroup()
local g1 = display.newGroup(); g0:insert(g1)
local g2 = display.newGroup(); g0:insert(g2)
local g3 = display.newGroup(); g0:insert(g3)
local r1 = display.newRect( 10, 10, 25, 25 ); g1:insert(r1)
local r2 = display.newRect( 20, 20, 25, 25 ); g1:insert(r2)
local r3 = display.newRect( 30, 30, 25, 25 ); g1:insert(r3)
local r4 = display.newRect( 40, 40, 25, 25 ); g2:insert(r4)
local r5 = display.newRect( 50, 50, 25, 25 ); g2:insert(r5)
local r6 = display.newRect( 60, 60, 25, 25 ); g2:insert(r6)
local r7 = display.newRect( 70, 70, 25, 25 ); g3:insert(r7)
local r8 = display.newRect( 80, 80, 25, 25 ); g3:insert(r8)
local r9 = display.newRect( 90, 90, 25, 25 ); g3:insert(r9)
– clear all
local stage = display.getCurrentStage()
for i = stage.numChildren, 1, -1 do
stage:remove(i)
end
– watch for leaks
collectgarbage()
print(collectgarbage(‘count’))
end)[/lua] [import]uid: 32962 topic_id: 7489 reply_id: 26723[/import]

@p120ph37: Thank you so much for your contributions as well! Surely that will come in very handy. I’ve already included it in my project to see how it works out :slight_smile: [import]uid: 7849 topic_id: 7489 reply_id: 26737[/import]