Titanium JIRA Archive
Alloy (ALOY)

[ALOY-897] Adding a Widget that uses arguments to a controller's XML causes a Proxy leak

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionFixed
Resolution Date2014-01-06T17:39:18.000+0000
Affected Version/sAlloy 1.2.2
Fix Version/sAlloy 1.4.0
ComponentsWidgets
Labelsqe-closed-3.3.0, qe-testadded
ReporterAlan Leard
AssigneeTony Lukasavage
Created2013-12-06T01:09:05.000+0000
Updated2014-05-14T09:52:13.000+0000

Description

Description

Proxies do not get released from a window that has a widget added to in in the XML (widget has to be using arguments).

Repro Steps

-Build attached app. -Open Xcode Instruments. -Open the new window, then close it. -Repeat several times and you will find that Proxies are retained while UIViews are released. -Comment out line 4 of LandingPage.xml to remove the widget, then repeat steps. The leak goes away.

Attachments

FileDateSize
SprintProxyTest.zip2013-12-06T01:09:05.000+00005071174

Comments

  1. Tony Lukasavage 2013-12-06

    Found the root of the issue, and Alan's hint that it required arguments being passed to the widget is what helped me find it. At the beginning of the loading widget's controller, all arguments are attached to the loading UI proxy, like this:
       var args = arguments[0] || {};
       
       for (var k in args) {
       	$.loading[k] = args[k];
       }
       
    This is meant to attach any arguments passed through to the actual loading UI proxy. What the developer of the widget doesn't see is that Alloy also attached hidden properties to the widget's argument list as well that help it understand things like where it is in the view hierarchy, or if it is part of data binding. In this particular case, the __parentSymbol property, which holds a reference to the parent of this widget, is being attached to the loading UI proxy. When the widget is removed, it retains this __parentSymbol reference unbeknownst to the developer, and in turn stays in memory since this reference is never nulled out, and hence the seemingly unavoidable memory leak. So technically this memory leak is not inherent in Alloy, but is a result of the widget attempting to iterate through all given arguments and attach them to the proxy without validation. It is actually really easy for the widget to be updated to avoid this. That said, the hidden properties Alloy attaches should not be enumerable, and this type of problem should not even be possible. Here's what will be done:

    Make a small modification to the loading widget to more deliberately handle the incoming arguments, which would prevent the memory leak in this widget and address the immediate problem.

    Use the Object.defineProperty() function to make Alloy's hidden variables non-enumerable, which would prevent any other widgets from falling victim to this trap.

  2. Tony Lukasavage 2013-12-06

    The loading widget, the only builtin Alloy widget affected by this, has been fixed in ALOY-898. The underlying issue yet to be addressed here can be worked around on of 2 ways in the meantime:

    Don't blindly iterate over the properties in the "args" object as they will contain Alloy hidden properties

    Use the following code snippet to safely ignore Alloy hidden properties when iterating over "args":

       var args = arguments[0] || {};
       
       for (var k in args) {
               // Ignore Alloy hidden properties to work around ALOY-897
               if (k === 'id' || /^(?:__|#|$)/.test(k)) { continue; }
       
               $.loading[k] = args[k];
       }
       
  3. Tony Lukasavage 2014-01-06

    PR: https://github.com/appcelerator/alloy/pull/294 Functional test can be run against the app given in the description to ensure that no memory leaks occur.
  4. Priya Agarwal 2014-05-14

    Verified the FIXED with: Appc-Studio:3.3.0.201405121247 sdk:3.3.0.v20140513191712 acs:1.0.14 alloy:1.4.0-dev npm:1.3.2 titanium:3.3.0-dev titanium-code-processor:1.1.1 xcode:5.1.1 Device:Iphone Simulator(v7.1) Ran the app found no memory leaks. Working as expected. Hence closing the issue.

JSON Source