05 May 2015
Tutorial: The Finalize API (an Unsung Hero)
Jason Schroeder is a New York City-based app developer and Corona Ambassador who has released seven Corona-made apps since he began using Corona SDK in 2011. He is currently working on an independent reboot of the 21-year-old Abrosia Software game “Chiral,” which he expects to release later this year. You can follow Jason on Twitter @schroederapps and learn more about Chiral at www.chiralgame.com.
There are over 1,000 Lua APIs for Corona SDK, according to the API Reference. While certain APIs, such as those found in the display, audio, and composer libraries are so essential that even the most “newbie” developers become instantly familiar with them, there’s a huge advantage to be gained by regularly browsing the online documentation and discovering hidden gems that you may not have noticed when teaching yourself the basics. One great example of this is the finalize event API, which gets scandalously little recognition given how useful it is.
Every Corona display object can (and does) dispatch “events” (which take the form of Lua tables) on a regular basis, even if you aren’t aware of these events or aren’t responding to them in your code. For example, if a user touches a display object, that object always dispatches an event table containing key/value pairs such as a reference to the dispatching object (
event.target) and the coordinates where the user’s finger is touching the screen (
event.y), among others. By adding a listener function to handle these dispatched events using the object:addEventListener() API, you can make your app respond to this user input. But even if you don’t add a listener for them, every display object in your app is dispatching “touch” events anytime the user’s finger happens to touch them.
Not unlike touch events, any time a display object is removed from the stage — usually by calling display.remove() or object:removeSelf() — that object dispatches a finalize event. I’d imagine that many Corona developers don’t bother handling these events, but being able to perform specific actions just as a display object is being destroyed has all sorts of useful applications. I’ll explore one specific example later on in this post, but first let’s go over how to listen for finalize events and handle the data that gets passed into our listener function.
Listening for Finalize Events
Just like touch listeners, a finalize listener can take the form of a “table listener” or a “function listener.” For the purposes of this tutorial, I’m going to focus on using a table listener because it’s my preferred method — but either type of listener will work, and you should use whatever method works best for you. If you don’t know the difference between these types of listeners, you can read more about it here.
Listening for finalize events requires 3 basic steps. First, we create our listener function:
local function finalizeListener( self, event )
-- do something when "self" is removed from the stage
print( "Object removed from stage: "..tostring(self) )
Next, we add a
.finalize property to a display object, pointing to our listener function:
object.finalize = finalizeListener
And finally, we add an event listener to that same object, listening for
object:addEventListener( "finalize" )
Now, whenever that display object is removed from the stage, our listener function will be triggered, and the console will print out the string
"Object removed from stage:" followed by the object’s table ID.
Why You Should Use Finalize Listeners
The code above shows how to listen for finalize events and respond to them, but it doesn’t really showcase why this is so useful. Honestly, the potential applications of finalize listeners are only limited by your imagination, and the usefulness will depend on the needs of your app — but let’s look at one specific scenario where a finalize listener can help keep your app running smoothly.
In some cases, you may need to have a display object update itself on every single frame of your app. For example, you could have a text object that displays the user’s score that needs to be constantly updated, or you might have a character sprite that needs to be repositioned on every frame to keep it centered on the screen. The best way to accomplish these continual updates is through a Runtime enterFrame listener, which is a function that is called every time your app draws a new “frame.”
While enterFrame listeners are great tools because they can be run as often as 60 times per second, they can also wreak havoc if you have any errors in your code. And if you attempt to manipulate a display object that has been destroyed in an enterFrame listener (or any
nil object), you’re going to get hit with a show-stopping Runtime error. This is avoided by removing your enterFrame listener before it can attempt to manipulate your soon-to-be-removed display object. While it’s possible to manage this manually, removing enterFrame listeners in the same block of code that destroys your display object, you can save yourself time and headaches by utilizing finalize listeners to automate this process for you, especially in circumstances where you will be removing lots of objects with regularity.
Below is a simple
main.lua that creates an app that does the following:
It creates a circle that can be dragged around the screen.
It uses an enterFrame listener to resize the circle as it is dragged, depending on how close it is to the center of the screen
Releasing your finger from the circle will remove the circle from the stage, using display.remove() and re-draw a new circle 500 milliseconds later.
-- forward declare functions
local onEnterFrame, finalizeListener, touchListener, createCircle
-- function to calculate distance between two points
local function getDistance ( x1, y1, x2, y2 )
local dx = x1 - x2
local dy = y1 - y2
return math.sqrt ( dx * dx + dy * dy )
-- enterFrame listener
function onEnterFrame( self, event )
local distance = getDistance( self.x, self.y, display.contentCenterX, display.contentCenterY )
local scale = 1 - distance*.005
self.xScale, self.yScale = scale, scale
-- finalize listener
function finalizeListener( self, event )
Runtime:removeEventListener( "enterFrame", self )
print( "Removed enterFrame Listener for "..tostring(self) )
timer.performWithDelay( 500, createCircle )
-- touch listener
function touchListener( self, event )
if event.phase == "began" then
display.getCurrentStage():setFocus( self, event.id )
self.hasFocus = true
elseif self.hasFocus then
self.x, self.y = event.x, event.y
if event.phase == "ended" or event.phase == "cancelled" then
self.hasFocus = false
display.getCurrentStage():setFocus( nil, event.id )
display.remove( self )
-- function to create a new circle and add listeners to it
local circle = display.newCircle( display.contentCenterX, display.contentCenterY, display.contentHeight*.075 )
circle.finalize = finalizeListener
circle:addEventListener( "finalize" )
circle.enterFrame = onEnterFrame
Runtime:addEventListener( "enterFrame", circle )
circle.touch = touchListener
circle:addEventListener( "touch" )
-- create the first circle
Since there is an enterFrame listener used to manipulate the circle, the app will crash if that function is run after removing the circle from the stage. To see this in action, simply comment out line 43 from the code above and run it in the Corona Simulator. As soon as you release your finger from the circle and the circle is removed, you’ll get the following Runtime error:
This crash occurs because line 43 is necessary to add our finalize listener to the circle, ensuring that the enterFrame listener is removed when the circle is destroyed. That little listener function is mighty helpful! Keep in mind, also, that the finalize listener in the above code is re-usable: you can add it to any display objects (not just the circle) by setting an object’s
.finalize property to “
finalizeListener" and adding a “
finalize" event listener to that object. If your app creates a lot of objects that need to be updated in an enterFrame listener, having just one finalize listener to handle cleanup of those enterFrame listeners can be a real sanity-saver.
This just shows one possible application of finalize listeners, but I hope it gets you thinking about ways you can use Corona’s 1,000+ APIs to streamline your code and help make your development work faster and easier to manage. If you have come up with any especially creative uses of the finalize event API, please share in the comments — happy coding!