finalize event happens when ?

Hi,

When did the finalize event happens ? Directly after the remoself call or at the end of the frame ??

local r=display.newRect( 0, 0, 100, 100) function r:finalize(event) self:removeEventListener( "finalize", self ) self=nil print( "finalize event happens - that should happens in first, not in second" ) end r:addEventListener( "finalize", r ) local function dod() print( "dod should happens in second, not in first" ) print( "r : ".. tostring(r).. " is not nil" ) end r:removeSelf() dod()

I have the impression that display objects are removed at the end of the frame, not exactly when we called the removeSelf function.

Sincerely,

Yvan.

  1. I think the display object is ‘essentially’ destroyed immediately.

  2. This seems more like  a question about the timing relationship between finalize() and other events that may be processed in the same frame.

This in turn will become a discussion about the timing relationship between individual actions in a frame.

What problem are you encountering that is causing you issues and leading you to ask this question?

  1. Your code is wrong.  Setting ‘self’ to nil does nothing.  ‘self’ is a temporary variable local to the listener that references the object. 

‘r’ is the variable you want to set to nil.

Try this to see what I mean:

local r = display.newRect( 0, 0, 100, 100 ) local function isValid( obj ) return (obj ~= nil and obj.removeSelf and type(obj.removeSelf) == "function" ) end function r:finalize(event) --self:removeEventListener( "finalize" ) -- NO NEED TO DO THIS; IT IS AUTOMATIC --self=nil DOES NOTHING print( "Deleting object. Still valid? ==\> " tostring(isValid( obj ) ) ) end; r:addEventListener( "finalize" ) -- passing r is redundant in this case local function dod() if( not isValid( r ) ) then return end print( "dod should happens in second, not in first" ) print( "r : ".. tostring(r).. " is not nil" ) end print( "Before deleting object. Still valid? ==\> " tostring(isValid( r ) ) ) display.remove( r ) -- Opinion: removeSelf() sucks. Don't use it. Use display.remove() print( "After deleting object. Still valid? ==\> " tostring(isValid( r ) ) ) dod()

it’s the same situation with “r” in replace self.

I asked it because a transition that deleted an object at the end of the transition, but the object have been removed before (  between the time-transition )

local r=display.newRect( 0, 0, 100, 100) function r:first() r:removeEventListener( "first", r ) print( "first happens" ) end r:addEventListener( "first", r ) function r:second() r:removeEventListener( "second", r ) print( "second happens" ) end r:addEventListener( "second", r ) function r:finalize(event) r:removeEventListener( "finalize", r ) r=nil print( "finalize event happens" ) end r:addEventListener( "finalize", r ) r:dispatchEvent( {name="first"} ) r:removeSelf() r:dispatchEvent( {name="second"} )

I was thinking that the finalize event was include inside the remove function.

result : 

> first happens

> second happens

> finalize event happens

With that code, the remove event is clearly plot on the end of the code.

you’re still coding this wrongly…  you didn’t take my advice :( 

I’ll post a code link in a bit.

OK.  I’m back.  Note: I’m not trying to be rude.  It just bugs when I see extra code where it isn’t needed.
 
That said, if your code makes sense to you, that is perfectly fine.  It works the way you coded it, but you’re doing more typing than  you need to. 
 
I made a tester framework with two tests.  I took you most recent code post and made it the second test in the tester.
https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2018/06/finalize.zip
 
It clearly shows that the finalize() listener is called at the very end of the current frame.   
 
So, it looks like the deletion of objects is deferred to then end of the frame which is very smart, because that would avoid all kinds of unpleasantness due to dangling references and listeners getting called on a destroyed object.
 
Note: I don’t think it was always this way, but I could be wrong.  I do think it used to be that display.remove() triggered an immediate removal.
 
This test:

local function isValid(obj) return( obj ~= nil and type(obj.removeSelf) == "function" ) end local curFrame = 1 local function enterFrame() curFrame = curFrame + 1 end; Runtime:addEventListener( "enterFrame", enterFrame ) -- ===================================================== local test = {} -- -- Example from yvandotet, but cleaned up a little -- function test.run() print("RUNNING TEST 2 @ ", getTimer()) print("Frame: ", curFrame) print("-----------\n") local r = display.newRect( 0, 0, 100, 100) function r:first() print( "first() - Frame: ", curFrame) print( "first() @ ", getTimer()) print( "Object is valid? ", isValid(self), getTimer() ) print("-----------\n") end; r:addEventListener( "first" ) function r:second() print( "second() - Frame: ", curFrame) print( "second() @ ", getTimer()) print("Object is valid? ", isValid(self), getTimer() ) print("-----------\n") end; r:addEventListener( "second" ) function r:finalize() -- does not get event argument print( "finalize() - Frame: ", curFrame) print( "finalize() - Object is valid? ", isValid(self), " @ " , getTimer() ) self:removeEventListener("first") self:removeEventListener("second") r = nil print("-----------\n") end; r:addEventListener( "finalize") print("Attempting to dispatch event 'first' @ " , getTimer() ) print("Attempting to dispatch event 'first' @ frame ", curFrame ) r:dispatchEvent( { name = "first" } ) print("Removing object @ " , getTimer() ) print("Removing object @ frame ", curFrame ) display.remove( r ) print("-----------\n") print("Attempting to dispatch event 'second' @ " , getTimer() ) print("Attempting to dispatch event 'second' @ frame ", curFrame ) if( r ) then r:dispatchEvent( { name = "second" } ) else print("Object is valid? ", isValid(obj), getTimer() ) print( "r == nil @ " , getTimer() ) end end return test

produces this output:
 

13:32:21.213 RUNNING TEST 2 @ 8.3 13:32:21.213 Frame: 1 13:32:21.213 ----------- 13:32:21.213 13:32:21.213 Attempting to dispatch event 'first' @ 8.3 13:32:21.213 Attempting to dispatch event 'first' @ frame 1 13:32:21.213 first() - Frame: 1 13:32:21.213 first() @ 8.3 13:32:21.213 Object is valid? true 8.4 13:32:21.213 ----------- 13:32:21.213 13:32:21.213 Removing object @ 8.4 13:32:21.213 Removing object @ frame 1 13:32:21.213 ----------- 13:32:21.213 13:32:21.213 Attempting to dispatch event 'second' @ 8.4 13:32:21.213 Attempting to dispatch event 'second' @ frame 1 13:32:21.213 second() - Frame: 1 13:32:21.213 second() @ 8.4 13:32:21.213 Object is valid? true 8.4 13:32:21.213 ----------- 13:32:21.213 13:32:21.224 finalize() - Frame: 1 13:32:21.224 finalize() - Object is valid? true @ 8.5 13:32:21.224 ----------- 13:32:21.224  

I noticed a error in the above code and am looking into it.  BRB

Bug fixed.  Download now.

When an object have a transition during he is removed, the transition continue :

I always build a delete function to my object to manage the project, because the finalize event is not called at the good time.

local r=display.newRect(0,0,100,100) r.delete=function() if r then if r.tr then transition.cancel(r.tr) ; r.tr=nil end r:removeSelf() // error here because r.removeSelf is nil, so no function can be called r=nil end end r.tr=transition.to(r,{time=10000,x=200,onComplete=r.delete}) timer.performWithDelay(5000,function() r:removeSelf() end)

Because of the excecution delay during a frame, I prefere delete the object immediately during the calculus process, not in the end of the frame.

local tableOfObjects = {} local r=display.newRect(0,0,100,100) ; tableOfObjects[r]=r // this reference in tableOfObjects will be managed too r.enterFrame=function() end //all my actions r.tr=transition.to(r,{time=2000,x=200,onComplete=r.delete}) // I use delete function at the end just to try if no bug arrived r:addEventListener( "anEvent", r ) // touch, collision, etc... Runtime:addEventListener("enterFrame",r) //I remove properly the actions here r.manageDelete=function() tableOfObjects[r]=nil if r.tr then transition.cancel( r.tr ) ; r.tr=nil end r:removeEventListener( "finalize", r ) r:removeEventListener( "anEvent", r ) Runtime:removeEventListener( "enterFrame", r ) end //this function delete the object immediately r.delete=function(fromFinalize) if not r.isDeleted then r.manageDelete() r.isDeleted=true if not fromFinalize then r:removeSelf() end // if I call the delete function with the removeSelf method, I don't removed twice end end function r:finalize(event) r.delete(true) r=nil end r:addEventListener( "finalize", r ) timer.performWithDelay(1000,function() r:removeSelf() end)

That’s look " a lot " but it is better like this : manage all the cancellation and removeEvent in one side, and then remove the object.

(_ Sorry about the vertical spacing of this post;  I’m having trouble with the formatting. _)

  1. In my experience this is not true. 

When an object have a transition during he is removed, the transition continue

 
Deleting display objects ends the transitions associated with that object. 
 
_ test1.lua _  in the example zip file I provided demonstrates this clearly.
 
 
 
2. You can easily cancel all transitions associated with a display object like this:

local obj = display.newCircle( ... ) transitions.to( obj, ... ) -- add a trantision to obj transitions.to( obj, ... ) -- add another trantision to obj transition.cancel( obj ) -- cancels both transitions
  1. If you want to remove a display object when a transition completes, just use display.remove

    transition.to( obj, { x = 100, onComplete = display.remove } )

  2. I’m not sure what this meant:
     

if I call the delete function with the removeSelf method, I don’t removed twice

However, if you’re saying you have issues with duplicate deletes, that is part of why I encourage all users to use display.remove() instead of obj:removeSelf()

Calling display.remove() twice is safe and has no negative side-effects.  Calling obj:removeSelf() twice will crash your game/app.

On the flip side, getting that crash tells you you’ve got a problem with your design.

  1. Your addEventListner() calls are all redundantly coded.

If you direclty attach a function to the object using the same name as the event, this is how you should write your code:

local obj = display.new... function obj.collision()( self, event ) ... end obj:addEventListener( "collision" ) -- DO NOT PASS ARG 2; It is implied that 'obj' has the function

However, if you are using a standalone function you do this:

local obj = display.new... local function collision( self, event ) ... end obj:addEventListener( "collision", collision )

It sounds from this discussion that what you really need to do is add some simple gating flags to your code instead of trying to bend over backwards to hijack the sequence in which things occurs.

This is an example of a gating flag:

local obj1 = display.new??? local obj2 = display.new??? -- Assume I added bodies to obj1 and obj2 local function onCollision( self, event ) local other = event.other if( self.dead ) then return false end if( not other.dead ) then other:setFillColor( 1, 0, 1 ) end self.dead = true display.remove( self ) return false end obj1.collision = onCollision obj2.collision = onCollision obj1:addEventListener( "collision" ) obj2:addEventListener( "collision" )

In this example, both obj1 and obj2 use the same code as their collision listener (saves memory), and in each case, the listeners are gated by the flag ‘dead’.

  • If ‘dead’ is true, the collision listener exits early.
  • On the very first collision event for obj1 and obj2, the respective object referred to ‘self’ is deleted and marked as ‘dead’
  • The ‘other’ object is then set to pink.

Note: In the case where obj1 and obj2 collide with each other, one will be deleted before the other.  So, if we  didn’t have this code around the setFillColor() call, we could get an error.

 if( not other.dead ) then ... end

The ‘dead’ flag is immediately visible and available to all code referencing the object immediately after the flag is set.  Thus, you can use it to gate critical events and execution if you can’t wait one frame for the normal cleanup mechanisms to kick in.

Note2:  Notice I don’t use a finalize function and I don’t remove the ‘collision’ event listener.  I don’t have to because it is removed automatically by the process of deleting the display object.

Only Runtime event listeners need to be manually removed when deleting a display object.

As usual excellent explanation @roaminggamer

Only one thing. I thought that to eliminate a body in the collision it was necessary to use a small timer.

https://docs.coronalabs.com/api/event/collision/index.html#gotchas

So I thought you forgot it in a hurry

I was about to report it here but I did a test and this did not give any problems.

My question is: better to use timer or is there anything I’m forgetting?

@maximo97,

If you need to manipulate the physics body of any objects (as a result of a collision) including the owner of the listener, you must do so after a short delay (i.e. using a timer.*).

Destroying the display object does not need to be delayed.

Destroying a display object with a body causes the object and body to be destroyed at the end of the frame.

ok thanks for that tips. I had suspicious about display.remove because we don’t know what is inside. That’s why I use removeSelf + nil duo.

local obj = display.new... function obj.collision()( self, event ) ... end obj:addEventListener( "collision" ) -- DO NOT PASS ARG 2; It is implied that 'obj' has the function

Don’t use capital letters please, it’s like screaming on someone lol.

 

I remember a note talking about this missing. They talked about error that can be involved due to future updates of the inner code, that can cause fatal error.

And in the official docs, they says to pass a second variable in any cases.

local obj = display.new... local function collision( self, event ) ... end obj:addEventListener( "collision", collision )

In my example I have variable in my object’s creator classes. So I need to build an obj:method function for each objects to scope variables.

thanks

re: 

Don't use capital letters please, it's like screaming on someone lol.

I use CAPS in code posts because there is no way to highlight the code comments.  i.e. There is no bold  to draw your attention to the comment.

  1. I think the display object is ‘essentially’ destroyed immediately.

  2. This seems more like  a question about the timing relationship between finalize() and other events that may be processed in the same frame.

This in turn will become a discussion about the timing relationship between individual actions in a frame.

What problem are you encountering that is causing you issues and leading you to ask this question?

  1. Your code is wrong.  Setting ‘self’ to nil does nothing.  ‘self’ is a temporary variable local to the listener that references the object. 

‘r’ is the variable you want to set to nil.

Try this to see what I mean:

local r = display.newRect( 0, 0, 100, 100 ) local function isValid( obj ) return (obj ~= nil and obj.removeSelf and type(obj.removeSelf) == "function" ) end function r:finalize(event) --self:removeEventListener( "finalize" ) -- NO NEED TO DO THIS; IT IS AUTOMATIC --self=nil DOES NOTHING print( "Deleting object. Still valid? ==\> " tostring(isValid( obj ) ) ) end; r:addEventListener( "finalize" ) -- passing r is redundant in this case local function dod() if( not isValid( r ) ) then return end print( "dod should happens in second, not in first" ) print( "r : ".. tostring(r).. " is not nil" ) end print( "Before deleting object. Still valid? ==\> " tostring(isValid( r ) ) ) display.remove( r ) -- Opinion: removeSelf() sucks. Don't use it. Use display.remove() print( "After deleting object. Still valid? ==\> " tostring(isValid( r ) ) ) dod()

it’s the same situation with “r” in replace self.

I asked it because a transition that deleted an object at the end of the transition, but the object have been removed before (  between the time-transition )

local r=display.newRect( 0, 0, 100, 100) function r:first() r:removeEventListener( "first", r ) print( "first happens" ) end r:addEventListener( "first", r ) function r:second() r:removeEventListener( "second", r ) print( "second happens" ) end r:addEventListener( "second", r ) function r:finalize(event) r:removeEventListener( "finalize", r ) r=nil print( "finalize event happens" ) end r:addEventListener( "finalize", r ) r:dispatchEvent( {name="first"} ) r:removeSelf() r:dispatchEvent( {name="second"} )

I was thinking that the finalize event was include inside the remove function.

result : 

> first happens

> second happens

> finalize event happens

With that code, the remove event is clearly plot on the end of the code.

you’re still coding this wrongly…  you didn’t take my advice :( 

I’ll post a code link in a bit.

OK.  I’m back.  Note: I’m not trying to be rude.  It just bugs when I see extra code where it isn’t needed.
 
That said, if your code makes sense to you, that is perfectly fine.  It works the way you coded it, but you’re doing more typing than  you need to. 
 
I made a tester framework with two tests.  I took you most recent code post and made it the second test in the tester.
https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2018/06/finalize.zip
 
It clearly shows that the finalize() listener is called at the very end of the current frame.   
 
So, it looks like the deletion of objects is deferred to then end of the frame which is very smart, because that would avoid all kinds of unpleasantness due to dangling references and listeners getting called on a destroyed object.
 
Note: I don’t think it was always this way, but I could be wrong.  I do think it used to be that display.remove() triggered an immediate removal.
 
This test:

local function isValid(obj) return( obj ~= nil and type(obj.removeSelf) == "function" ) end local curFrame = 1 local function enterFrame() curFrame = curFrame + 1 end; Runtime:addEventListener( "enterFrame", enterFrame ) -- ===================================================== local test = {} -- -- Example from yvandotet, but cleaned up a little -- function test.run() print("RUNNING TEST 2 @ ", getTimer()) print("Frame: ", curFrame) print("-----------\n") local r = display.newRect( 0, 0, 100, 100) function r:first() print( "first() - Frame: ", curFrame) print( "first() @ ", getTimer()) print( "Object is valid? ", isValid(self), getTimer() ) print("-----------\n") end; r:addEventListener( "first" ) function r:second() print( "second() - Frame: ", curFrame) print( "second() @ ", getTimer()) print("Object is valid? ", isValid(self), getTimer() ) print("-----------\n") end; r:addEventListener( "second" ) function r:finalize() -- does not get event argument print( "finalize() - Frame: ", curFrame) print( "finalize() - Object is valid? ", isValid(self), " @ " , getTimer() ) self:removeEventListener("first") self:removeEventListener("second") r = nil print("-----------\n") end; r:addEventListener( "finalize") print("Attempting to dispatch event 'first' @ " , getTimer() ) print("Attempting to dispatch event 'first' @ frame ", curFrame ) r:dispatchEvent( { name = "first" } ) print("Removing object @ " , getTimer() ) print("Removing object @ frame ", curFrame ) display.remove( r ) print("-----------\n") print("Attempting to dispatch event 'second' @ " , getTimer() ) print("Attempting to dispatch event 'second' @ frame ", curFrame ) if( r ) then r:dispatchEvent( { name = "second" } ) else print("Object is valid? ", isValid(obj), getTimer() ) print( "r == nil @ " , getTimer() ) end end return test

produces this output:
 

13:32:21.213 RUNNING TEST 2 @ 8.3 13:32:21.213 Frame: 1 13:32:21.213 ----------- 13:32:21.213 13:32:21.213 Attempting to dispatch event 'first' @ 8.3 13:32:21.213 Attempting to dispatch event 'first' @ frame 1 13:32:21.213 first() - Frame: 1 13:32:21.213 first() @ 8.3 13:32:21.213 Object is valid? true 8.4 13:32:21.213 ----------- 13:32:21.213 13:32:21.213 Removing object @ 8.4 13:32:21.213 Removing object @ frame 1 13:32:21.213 ----------- 13:32:21.213 13:32:21.213 Attempting to dispatch event 'second' @ 8.4 13:32:21.213 Attempting to dispatch event 'second' @ frame 1 13:32:21.213 second() - Frame: 1 13:32:21.213 second() @ 8.4 13:32:21.213 Object is valid? true 8.4 13:32:21.213 ----------- 13:32:21.213 13:32:21.224 finalize() - Frame: 1 13:32:21.224 finalize() - Object is valid? true @ 8.5 13:32:21.224 ----------- 13:32:21.224  

I noticed a error in the above code and am looking into it.  BRB

Bug fixed.  Download now.