{ "id": "81218", "key": "TIMOB-5465", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "12081", "description": "", "name": "Sprint 2011-41", "archived": true, "released": true, "releaseDate": "2011-10-17" }, { "id": "11331", "description": "", "name": "Release 1.8.0", "archived": true, "released": true, "releaseDate": "2011-10-31" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2011-10-10T09:17:09.000+0000", "created": "2011-10-07T01:29:45.000+0000", "priority": { "name": "High", "id": "2" }, "labels": [ "branch-5062", "module_tabgroup", "qe-testadded" ], "versions": [ { "id": "11331", "description": "", "name": "Release 1.8.0", "archived": true, "released": true, "releaseDate": "2011-10-31" } ], "issuelinks": [], "assignee": { "name": "stephentramer", "key": "stephentramer", "displayName": "Stephen Tramer", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2014-06-19T12:46:32.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "description": "h1. Problem\r\nTapping the tab's button to return to the root window of the tab orphans any windows between the current window and the root window.\r\n\r\nh2. Why This Is A Problem\r\nOrphaned windows equate to leaked memory.\r\n\r\nh2. Expected Behavior\r\nTapping the tab's button should close every window that is open on that tab's stack, not just the current window.\r\n\r\nh2. Reproduction\r\nDrop the following in an app.js. Touch \"S1: Touch Me\", then \"S2: Touch Me\". Notice in the log which windows were opened, and which were closed.\r\n{code:title=app.js}\r\nvar tabGroup = Ti.UI.createTabGroup();\r\n\r\nfunction openWindowOnStack(i) {\r\n var win = Ti.UI.createWindow({\r\n title: 'I Am ' + i\r\n });\r\n win.addEventListener('open', function() {\r\n Ti.API.info('opened ' + i + '!');\r\n if (i < 4) {\r\n openWindowOnStack(i + 1);\r\n }\r\n });\r\n win.addEventListener('close', function() {\r\n Ti.API.info('closed ' + i + '!');\r\n });\r\n tab.open(win);\r\n}\r\n\r\nvar outer = Ti.UI.createWindow({\r\n title: 'I Am Parent',\r\n rightNavButton: Ti.UI.createButton({ title: 'S1: Touch Me' })\r\n});\r\nvar tab = Ti.UI.createTab({\r\n window: outer, title: 'S2: Touch Me'\r\n});\r\ntabGroup.addTab(tab);\r\n\r\nouter.rightNavButton.addEventListener('click', function() {\r\n openWindowOnStack(0);\r\n});\r\n\r\ntabGroup.open();\r\n{code}\r\n\r\nh2. Current Output\r\n{code:title=Current Output}\r\n[INFO] opened 0!\r\n[INFO] opened 1!\r\n[INFO] opened 2!\r\n[INFO] opened 3!\r\n[INFO] opened 4!\r\n[INFO] closed 4!\r\n{code}\r\n\r\nh2. Expected Output\r\n{code:title=Expected Output}\r\n[INFO] opened 0!\r\n[INFO] opened 1!\r\n[INFO] opened 2!\r\n[INFO] opened 3!\r\n[INFO] opened 4!\r\n[INFO] closed 3!\r\n[INFO] closed 2!\r\n[INFO] closed 1!\r\n[INFO] closed 0!\r\n{code}", "attachment": [], "flagged": false, "summary": "iOS: Tapping Tab Orphans Windows on Tab's Stack", "creator": { "name": "dtoth", "key": "dtoth", "displayName": "Dawson Toth", "active": true, "timeZone": "America/New_York" }, "subtasks": [], "reporter": { "name": "dtoth", "key": "dtoth", "displayName": "Dawson Toth", "active": true, "timeZone": "America/New_York" }, "environment": null, "comment": { "comments": [ { "id": "168611", "author": { "name": "dtoth", "key": "dtoth", "displayName": "Dawson Toth", "active": true, "timeZone": "America/New_York" }, "body": "h2. Workaround\r\nBy following the three rules at the top of this app.js, I am able to clean up any orphaned windows.\r\n{code:title=app.js}\r\n/*\r\n To keep track of and clean up windows that have been opened on a specific tab, you must do the following:\r\n - Whenever you open a window, use the \"openInTab(tab, win)\" function.\r\n - On every window, listen for its \"close\" event and call the \"trackClosedWindow(tab, win)\" function.\r\n - Whenever you want to close all but the root window of a tab, call the \"closeAllInTab(tab)\" function.\r\n - Whenever a root window gains focus, it must call \"closeAllInTab(tab)\" on its tab.\r\n */\r\n\r\nfunction openWindowOnStack(i) {\r\n var win = Ti.UI.createWindow({\r\n title: 'Window ' + i\r\n });\r\n win.addEventListener('open', function(evt) {\r\n Ti.API.info('Opened: ' + evt.source.title);\r\n if (i < 4) {\r\n openWindowOnStack(i + 1);\r\n }\r\n });\r\n win.addEventListener('close', function(evt) {\r\n Ti.API.info('Closed: ' + evt.source.title);\r\n trackClosedWindow(tab, evt.source);\r\n });\r\n openInTab(tab, win);\r\n}\r\n\r\nvar outer = Ti.UI.createWindow({\r\n title: 'Root Window',\r\n rightNavButton: Ti.UI.createButton({ title: 'S1: Touch Me' })\r\n});\r\nouter.addEventListener('focus', function() {\r\n // Make sure all children have closed...\r\n closeAllInTab(tab);\r\n});\r\nouter.rightNavButton.addEventListener('click', function() {\r\n openWindowOnStack(0);\r\n});\r\n\r\nfunction trackClosedWindow(tab, window) {\r\n var windows = tab.windows || [];\r\n // Iterate through backwards because windows further down the tree are more likely to be removed.\r\n for (var w = windows.length - 1; w >= 0; w--) {\r\n if (windows[w] == window) {\r\n windows[w] = null;\r\n windows.splice(w, 1);\r\n break;\r\n }\r\n }\r\n tab.windows = windows;\r\n}\r\nfunction closeAllInTab(tab) {\r\n var windows = tab.windows || [];\r\n // Iterate through forwards because we want to close the most recent window last for a nice animation to the root.\r\n for (var w = 0; w < windows.length; w++) {\r\n windows[w].close();\r\n windows[w] = null;\r\n }\r\n tab.windows = windows = [];\r\n}\r\nfunction openInTab(tab, window) {\r\n var windows = tab.windows || [];\r\n windows.push(window);\r\n tab.open(window);\r\n tab.windows = windows;\r\n}\r\n\r\nvar tabGroup = Ti.UI.createTabGroup();\r\nvar tab = Ti.UI.createTab({\r\n window: outer,\r\n title: 'S2. Touch Me'\r\n});\r\ntabGroup.addTab(tab);\r\ntabGroup.open();\r\n{code}", "updateAuthor": { "name": "dtoth", "key": "dtoth", "displayName": "Dawson Toth", "active": true, "timeZone": "America/New_York" }, "created": "2011-10-10T05:02:54.000+0000", "updated": "2011-10-10T05:03:36.000+0000" }, { "id": "171508", "author": { "name": "thomashuelbert", "key": "thomashuelbert", "displayName": "Thomas Huelbert", "active": true, "timeZone": "America/Los_Angeles" }, "body": "note to QE - this may not fit easily into current template.", "updateAuthor": { "name": "thomashuelbert", "key": "thomashuelbert", "displayName": "Thomas Huelbert", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2011-11-04T09:44:27.000+0000", "updated": "2011-11-04T09:44:27.000+0000" }, { "id": "175475", "author": { "name": "mpettiford", "key": "mpettiford", "displayName": "Michael Pettiford", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Tested on Ti Studio 1.0.7.201112080131\r\n
Ti Mob SDK 1.8.0.1.v20111208124750\r\n
OSX Lion\r\niPhone 4S OS 5.0.1\r\n\r\nExpected behavior is shown", "updateAuthor": { "name": "mpettiford", "key": "mpettiford", "displayName": "Michael Pettiford", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2011-12-08T14:41:46.000+0000", "updated": "2011-12-08T14:41:46.000+0000" } ], "maxResults": 5, "total": 5, "startAt": 0 } } }