I just fixed a serious memory leak in an Alloy widget, caused by carelessly passing widget arguments to a view’s applyProperties
method. This blog explains the cause and fix for this leak.
Imagine having a useless widget with a view like this:
1 2 3 |
<Alloy> <Label id="myLabel" /> </Alloy> |
And a controller like this:
1 2 |
var args = arguments[0] || {}; $.myLabel.applyProperties(args); |
Now you would think it’s no problem to require the widget and directly pass any properties for it’s label like this:
1 2 3 4 5 |
<Alloy> <Window> <Widget src="myWidget" color="red" /> </Window> </Alloy> |
The resulting Alloy compiled JS for this view would be something like:
1 2 3 4 5 6 7 |
$.__views.__alloyId1 = Ti.UI.createWindow(); $.__views.__alloyId1 && $.addTopLevelView($.__views.__alloyId1); $.__views.__alloyId2 = Alloy.createWidget("myWidget", "widget", { color: "red", id: "__alloyId2", __parentSymbol: $.__views.__alloyId1 }); |
The cause
As you’ll note, Alloy passes a special __parentSymbol
property to the widget, referencing it’s parent view – our window. Now because in the widget’s controller we just pass all arguments to the label’s applyProperties
method, the label will now hold a reference to the window as well. And not just the JavaScript object, but also it’s native Objective-C or JAVA counterpart or so-called proxy object. When we later close the window, the two underlaying proxy objects will keep each other hostage and will never be cleared from memory.
The solution
Well, we have to get rid of the reference! You can’t use Underscore’s _.clone
method for to make sure you have copies for all properties, since it only does a shallow clone. A better way would just to simply remove __parentSymbol
, for example by using ._omit
. And while you’re at it, you might as well omit the special id
, $model
and new __itemTemplate
properties as well :)
1 2 |
var args = arguments[0] || {}; $.myLabel.applyProperties(_.omit(args, 'id', '__parentSymbol', '__itemTemplate', '$model')); |