ShawnCBerg
ShawnCBerg

Reputation: 594

Titanium Application events firing after controller is destroyed

parent_controller.js:

_.each(category, function(inventory_item, index, list) {

    var row = Alloy.createController('inventory_list_row', {
        selectedBackgroundColor: '',
        data: inventory_item
    });

    row.destroy();
    row = null;

});

Ti.App.fireEvent('checkIn');

inventory_list_row.js:

Ti.App.addEventListener('checkIn', function(e) {
    console.info('Checking In: ' + args.data.title);
});

Preface: The above code is watered down to prove a point. I know it's doesn't really do anything, but it does prove problematic.

The code in parent_controller.js can be executed multiple times based upon user interaction in my Titanium Mobile iPad application. If the code above only runs once everything is fine. Each time the code above runs again the previous controllers are some how remaining in memory and are still trapping events.

For example, let's say there are 3 inventory_list_row controllers generated the first time the code is executed. In the console I'll see 3 'Checking In' messages appear as expected. The second time it runs, though, I'll see 6 'Checking In' messages appear in the console, so on and so forth.

Why is this, and what can I do to prevent this? You can see I've tried using .destroy and setting row to null to no avail.

Upvotes: 4

Views: 3686

Answers (2)

Josiah Hester
Josiah Hester

Reputation: 6095

Heres a quick fix, implementing what Aaron said in his first bullet, just add this method to your inventory row controller.

// Here is the event listener function
var checkInListenFunction = function(e) {
    console.info('Checking In: ' + args.data.title);
}

// Add just like in your code
Ti.App.addEventListener('checkIn', checkInListenFunction);

// When this controller is destroyed you have to remove the listener as well
// OR the controller will forever stay in memory, since you access variables 
// from the controllers scope!
exports.destroy = function() {
    // Remove the listener first
    Ti.App.addRemoveListener('checkIn', checkInListenFunction);
    $.destroy();
}

Global event listeners persist until you remove them, that means that (according to javascript scope rules) any variable they access is automatically persisted as well. Since you access args.data.title inside your event listener, and this variable is scoped to the inventory_list_row controller, the interpreter keeps the controller (or most of it) around in memory no matter what you do, so these events will continue to fire long after you destroy and null them out.

I would not use global event listeners at all, too much scope confusion and potential for memory leaks in a memory scarce environment. Take Aaron's suggestions and make this callback based, or fire a local event on the controller itself.

EDIT:

You could do this if you only want that listener to "listen" one time, just remove it immediately, simple:

Ti.App.addEventListener('checkIn', function(e) {
    console.info('Checking In: ' + args.data.title);
    Ti.App.removeEventListener(this);
});

Upvotes: 0

Aaron Saunders
Aaron Saunders

Reputation: 33345

  • You may need to remove the eventListener in the destroy method of the controller.
  • why are you using a global event listener for a local event management?
  • maybe you can use a callback instead

These are just some quick observations without me writing any code to veryify. Honestly I do not use global events at all.

Upvotes: 1

Related Questions