OOP/ Runtime and System Events

Hi.

Is there a clean way of dispatching events to object instances ?

e.g. I want to do something like

Runtime:addEventListener(“accelerometer”,someObject)

and have someObject have a method

function someClass:accelerometer(event)

that responds to it. Table listeners work (if you make the : a .) but there appears to be no simple way of setting ‘self’, so to speak. The best solution I have that works is a local function e.g.

Runtime:addEventListener(“accelerometer”,function(e) someInstance:accelerometer(e) end_)_

For touch events the object instance could be stored as a reference in the display object and grabbed from event.target but this seems to be a major hack :slight_smile:

you’re close, but just need to perhaps formalize/generalize the process a little bit.
you create a closure as you have done, but you will need to save that on your object for use later, on ‘removeEventListener’.
 

i have this function in my utilities module to create closures for object-callback as required:
 

-- createObjectCallback() -- Creates a closure used to bind a method to an object. Useful for creating a custom callback. -- -- @param object the object which has the method -- @param method the method to call -- function Utils.createObjectCallback( object, method ) if object == nil or method == nil then error( "ERROR: missing object or method in createObjectCallback()" ) end return function( ... ) return method( object, ... ) end end

here’s an example of its use:

function YourObject:setup() self.\_callback = Utils.createObjectCallback( self, self.runtimeEventHandler ) -- \<\< Runtime:addEventListener( 'accelerometer', self.\_callback ) end function YourObject:teardown() Runtime:removeEventListener( 'accelerometer', self.\_callback ) self.\_callback = nil end function YourObject:runtimeEventHandler( event ) -- this method will now get runtime events -- in context of instances of YourObject end

since i use this so much, i added it as a class method to my OOP library:
 

-- in OOP hierarchy function Object:createCallback( method ) if method == nil then error( "ERROR: missing method in createCallback()", 2 ) end return function( ... ) return method( self, ... ) end end

this style makes the callback creation a littler simpler, but everything else is the same:

function YourObject:setup() self.\_callback = self:createCallback( self.runtimeEventHandler ) -- \<\< note use of : and . Runtime:addEventListener( 'accelerometer', self.\_callback ) end function YourObject:teardown() Runtime:removeEventListener( 'accelerometer', self.\_callback ) self.\_callback = nil end function YourObject:runtimeEventHandler( event ) -- this method will now get runtime events -- in context of instances of YourObject end

HTH

cheers,
dmc

[oops accidentally deleted wrong post, reposting from memory]
Is it specifically the accelerometer event that doesn’t work? If so, that’d seem a bug, because isn’t that exactly what table listeners were designed to do? fe:

local t = {} t.name = "I am self, the instance" function t:enterFrame(event) -- aka: t.enterFrame(self,event) print("self: " .. self.name .. " event: " .. event.name) end Runtime:addEventListener("enterFrame", t)

or, same thing functionally, with OOP aspect elaborated:

local clas = {} function clas:new() local inst = {} setmetatable(inst, {\_\_index=clas}) inst.name = "I am self, the instance" return inst end function clas:enterFrame(event) print("self: " .. self.name .. " event: " .. event.name) end local inst = clas:new() Runtime:addEventListener("enterFrame", inst)

all event callbacks should work, either with a closure or table listener. it doesn’t matter. 

though i rarely use table listeners. partly because i’d prefer to call my own methods/names for organization/consistency. mostly because OO code starts to break down when there’s more than one object involved that gives the same event, e.g., ‘touch’ or custom events – this a much more likely situation in the apps i’ve made. in these situations, the solution using table listeners starts to get away from OO, which is the whole idea in the first place. :slight_smile:

that said, with less-used events like ‘accelerometer’ and ‘enterFrame’, i would consider using a table listener. :slight_smile:

IME creating closures should always be done very carefully. Function listeners (sans closures) are not much use for OOP

dmccuskey,

you can always make method that creates table listener. It helps me organize response from multiple components on same event type.

– very rough draft, to show the concept

function class:createTouchListener( self , some, fancy, arguments ) local listener = { touch = function( this , event ) -- this here is reference to table listener, self is reference to instance print( some , fancy , arguments, event.target , self ) end, } return listener end -------- instance.rectangleListener = instance:createTouchListener( "some" , "fancy", "argument") instance.ovalListener = instance:createTouchListener( "some" , "fancy", "argument") instance.rectangle:addEventListener( instance.rectangleListener ) instance.oval:addEventListener( instance.ovalListener )

I want to do something like
 
Runtime:addEventListener(“accelerometer”,someObject)
 
and have someObject have a method
 
function someClass:accelerometer(event)

If OP doesn’t yet have an answer, then maybe a restatement of the problem is needed?

There are lots of clever solutions here, but for what you originally asked, none of them should be necessary - that functionality is already there, and works, in the form of table listeners, no closures necessary anywhere. (But I know the OP is proficient, so I at least have probably missed his intent, cuz I doubt the question was really intended to be that simple.)

If you DO need a closure, for whatever reason, then dmccuskey’s approach is correct - you should store a reference for later removal. If it helps, his example (roughly) boils down to:

-- not this: Runtime:addEventListener("someEvent", function(e) inst:someEventListener(e) end) --because later: Runtime:removeEventListener("someEvent", wtf?? -- but this: inst.someEventClosure = function(e) inst:someEventListener(e) end Runtime:addEventListener("someEvent", inst.someEventClosure) Runtime:removeEventListener("someEvent", inst.someEventClosure)

delwing’s approach is also equivalent and correct btw. there are any number of ways you might do same, but the key to a ‘correct’ solution will be keeping a reference to that closure. closure’s are usually only a source of trouble when anonymous - closures themselves aren’t inherently evil.

fwiw, hth

you’re close, but just need to perhaps formalize/generalize the process a little bit.
you create a closure as you have done, but you will need to save that on your object for use later, on ‘removeEventListener’.
 

i have this function in my utilities module to create closures for object-callback as required:
 

-- createObjectCallback() -- Creates a closure used to bind a method to an object. Useful for creating a custom callback. -- -- @param object the object which has the method -- @param method the method to call -- function Utils.createObjectCallback( object, method ) if object == nil or method == nil then error( "ERROR: missing object or method in createObjectCallback()" ) end return function( ... ) return method( object, ... ) end end

here’s an example of its use:

function YourObject:setup() self.\_callback = Utils.createObjectCallback( self, self.runtimeEventHandler ) -- \<\< Runtime:addEventListener( 'accelerometer', self.\_callback ) end function YourObject:teardown() Runtime:removeEventListener( 'accelerometer', self.\_callback ) self.\_callback = nil end function YourObject:runtimeEventHandler( event ) -- this method will now get runtime events -- in context of instances of YourObject end

since i use this so much, i added it as a class method to my OOP library:
 

-- in OOP hierarchy function Object:createCallback( method ) if method == nil then error( "ERROR: missing method in createCallback()", 2 ) end return function( ... ) return method( self, ... ) end end

this style makes the callback creation a littler simpler, but everything else is the same:

function YourObject:setup() self.\_callback = self:createCallback( self.runtimeEventHandler ) -- \<\< note use of : and . Runtime:addEventListener( 'accelerometer', self.\_callback ) end function YourObject:teardown() Runtime:removeEventListener( 'accelerometer', self.\_callback ) self.\_callback = nil end function YourObject:runtimeEventHandler( event ) -- this method will now get runtime events -- in context of instances of YourObject end

HTH

cheers,
dmc

[oops accidentally deleted wrong post, reposting from memory]
Is it specifically the accelerometer event that doesn’t work? If so, that’d seem a bug, because isn’t that exactly what table listeners were designed to do? fe:

local t = {} t.name = "I am self, the instance" function t:enterFrame(event) -- aka: t.enterFrame(self,event) print("self: " .. self.name .. " event: " .. event.name) end Runtime:addEventListener("enterFrame", t)

or, same thing functionally, with OOP aspect elaborated:

local clas = {} function clas:new() local inst = {} setmetatable(inst, {\_\_index=clas}) inst.name = "I am self, the instance" return inst end function clas:enterFrame(event) print("self: " .. self.name .. " event: " .. event.name) end local inst = clas:new() Runtime:addEventListener("enterFrame", inst)

all event callbacks should work, either with a closure or table listener. it doesn’t matter. 

though i rarely use table listeners. partly because i’d prefer to call my own methods/names for organization/consistency. mostly because OO code starts to break down when there’s more than one object involved that gives the same event, e.g., ‘touch’ or custom events – this a much more likely situation in the apps i’ve made. in these situations, the solution using table listeners starts to get away from OO, which is the whole idea in the first place. :slight_smile:

that said, with less-used events like ‘accelerometer’ and ‘enterFrame’, i would consider using a table listener. :slight_smile:

IME creating closures should always be done very carefully. Function listeners (sans closures) are not much use for OOP

dmccuskey,

you can always make method that creates table listener. It helps me organize response from multiple components on same event type.

– very rough draft, to show the concept

function class:createTouchListener( self , some, fancy, arguments ) local listener = { touch = function( this , event ) -- this here is reference to table listener, self is reference to instance print( some , fancy , arguments, event.target , self ) end, } return listener end -------- instance.rectangleListener = instance:createTouchListener( "some" , "fancy", "argument") instance.ovalListener = instance:createTouchListener( "some" , "fancy", "argument") instance.rectangle:addEventListener( instance.rectangleListener ) instance.oval:addEventListener( instance.ovalListener )

I want to do something like
 
Runtime:addEventListener(“accelerometer”,someObject)
 
and have someObject have a method
 
function someClass:accelerometer(event)

If OP doesn’t yet have an answer, then maybe a restatement of the problem is needed?

There are lots of clever solutions here, but for what you originally asked, none of them should be necessary - that functionality is already there, and works, in the form of table listeners, no closures necessary anywhere. (But I know the OP is proficient, so I at least have probably missed his intent, cuz I doubt the question was really intended to be that simple.)

If you DO need a closure, for whatever reason, then dmccuskey’s approach is correct - you should store a reference for later removal. If it helps, his example (roughly) boils down to:

-- not this: Runtime:addEventListener("someEvent", function(e) inst:someEventListener(e) end) --because later: Runtime:removeEventListener("someEvent", wtf?? -- but this: inst.someEventClosure = function(e) inst:someEventListener(e) end Runtime:addEventListener("someEvent", inst.someEventClosure) Runtime:removeEventListener("someEvent", inst.someEventClosure)

delwing’s approach is also equivalent and correct btw. there are any number of ways you might do same, but the key to a ‘correct’ solution will be keeping a reference to that closure. closure’s are usually only a source of trouble when anonymous - closures themselves aren’t inherently evil.

fwiw, hth