{ "id": "122850", "key": "TIMOB-15885", "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": [], "resolution": { "id": "3", "description": "The problem is a duplicate of an existing issue.", "name": "Duplicate" }, "resolutiondate": "2014-02-18T22:39:46.000+0000", "created": "2013-11-23T21:29:33.000+0000", "priority": { "name": "Low", "id": "4" }, "labels": [ "SupportTeam" ], "versions": [ { "id": "15593", "description": "Release 3.1.3", "name": "Release 3.1.3", "archived": true, "released": true, "releaseDate": "2013-09-18" } ], "issuelinks": [ { "id": "35218", "type": { "id": "10002", "name": "Duplicate", "inward": "is duplicated by", "outward": "duplicates" }, "outwardIssue": { "id": "122849", "key": "TIMOB-15889", "fields": { "summary": "iOS: Memory leak on iOS when items in section are changed on ListView after ListView has been created", "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" } }, "priority": { "name": "High", "id": "2" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } } ], "assignee": { "name": "ingo", "key": "ingo", "displayName": "Ingo Muschenetz", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2017-03-21T16:57:11.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": "h2. Problem\r\n\r\niOS: Memory leak on iOS ListView when template contains event listener.\r\n(Similar to Android issue TIMOB-13747)\r\n\r\nh2. Test case\r\n\r\n- Run the app below in Instruments and profile memory usage\r\n- With every opening of the window containing the list view, 8 TiUILabelProxy and 4 TiUIListItemProxy objects are allocated and never freed again.\r\n\r\n\r\n{code:lang=javascript|title=app.js}\r\nvar baseWin = Ti.UI.createWindow({backgroundColor: 'red'});\r\n\r\nvar b = Ti.UI.createButton({\r\n\ttitle: 'Click',\r\n\tcolor: 'red',\r\n\tselectedColor: 'blue',\r\n\theight: '100dp',\r\n\twidth: '150dp',\r\n\tbackgroundImage: 'appicon.png',\r\n\tbackgroundColor: 'green',\r\n\tstyle: Titanium.UI.iPhone ? Titanium.UI.iPhone.SystemButtonStyle.PLAIN : 0 \r\n});\r\n\r\n\r\nb.addEventListener('click', function(evt) {\r\n\r\n\tvar win = Ti.UI.createWindow({backgroundColor: 'white'});\r\n\r\n\t\r\n\t// Create a custom template that displays an image on the left, \r\n\t// then a title next to it with a subtitle below it.\r\n\tvar myTemplate = {\r\n\t childTemplates: [\r\n\t { // Title \r\n\t type: 'Ti.UI.Label', // Use a label for the title \r\n\t bindId: 'info', // Maps to a custom info property of the item data\r\n\t properties: { // Sets the label properties\r\n\t color: 'black',\r\n\t font: { fontFamily:'Arial', fontSize: '20dp', fontWeight:'bold' },\r\n\t left: '60dp', top: 0,\r\n\t },\r\n\t events: {\r\n\t \t'click': function(evt) {\r\n\t \t\tvar item = evt.section.getItemAt(evt.itemIndex);\r\n\t \t\titem.info.text = 'Clicked';\r\n\t \t\tevt.section.updateItemAt(evt.itemIndex, item);\r\n\t \t}\r\n\t }\r\n\t },\r\n\t { // Subtitle\r\n\t type: 'Ti.UI.Label', // Use a label for the subtitle\r\n\t bindId: 'es_info', // Maps to a custom es_info property of the item data\r\n\t properties: { // Sets the label properties\r\n\t color: 'gray',\r\n\t font: { fontFamily:'Arial', fontSize: '14dp' },\r\n\t left: '60dp', top: '25dp',\r\n\t }\r\n\t }\r\n\t ]\r\n\t};\r\n\t\r\n\tvar listView = Ti.UI.createListView({\r\n\t // Maps myTemplate dictionary to 'template' string\r\n\t templates: { 'template': myTemplate },\r\n\t // Use 'template', that is, the myTemplate dict created earlier\r\n\t // for all items as long as the template property is not defined for an item.\r\n\t defaultItemTemplate: 'template'\r\n\t});\r\n\tvar sections = [];\r\n\t\r\n\tvar fruitSection = Ti.UI.createListSection({ headerTitle: 'Fruits / Frutas'});\r\n\tvar fruitDataSet = [\r\n\t // the text property of info maps to the text property of the title label\r\n\t // the text property of es_info maps to text property of the subtitle label\r\n\t // the image property of pic maps to the image property of the image view\r\n\t { info: {text: 'Apple'}, es_info: {text: 'Manzana'}, pic: {image: 'apple.png'}},\r\n\t { info: {text: 'Banana'}, es_info: {text: 'Banana'}, pic: {image: 'banana.png'}}\r\n\t];\r\n\tfruitSection.setItems(fruitDataSet);\r\n\tsections.push(fruitSection);\r\n\t\r\n\tvar vegSection = Ti.UI.createListSection({ headerTitle: 'Vegetables / Verduras'});\r\n\tvar vegDataSet = [\r\n\t { info: {text: 'Carrot'}, es_info: {text: 'Zanahoria'}, pic: {image: 'carrot.png'}},\r\n\t { info: {text: 'Potato'}, es_info: {text: 'Patata'}, pic: {image: 'potato.png'}}\r\n\t];\r\n\tvegSection.setItems(vegDataSet);\r\n\tsections.push(vegSection);\r\n\r\n\tlistView.setSections(sections);\r\n\t\r\n\t\r\n\tvar button = Ti.UI.createButton({\r\n\t\ttitle: 'Close Window',\r\n\t\tcolor: 'black',\r\n\t\theight: '100dp',\r\n\t\twidth: '150dp',\r\n\t\tbackgroundColor: 'green',\r\n\t});\r\n\t\r\n\tbutton.addEventListener('click', function(evt) {\r\n\t\twin.close();\r\n\t\twin.remove(listView);\r\n\t\tlistView = null;\r\n\t\twin.remove(button);\r\n\t\tbutton = null;\r\n\t\twin = null;\r\n\t});\t\r\n\t\r\n\twin.add(listView);\r\n\twin.add(button);\r\n\twin.open();\r\n\t\r\n});\r\n\r\nbaseWin.add(b);\r\nbaseWin.open();\r\n{code}\r\n\r\n", "attachment": [], "flagged": false, "summary": "iOS: Memory leak on iOS ListView when template contains event listener", "creator": { "name": "philet", "key": "philet", "displayName": "Philippe Wueger", "active": true, "timeZone": "Europe/Berlin" }, "subtasks": [], "reporter": { "name": "philet", "key": "philet", "displayName": "Philippe Wueger", "active": true, "timeZone": "Europe/Berlin" }, "environment": "Titanium 3.1.3\r\niOS SDK 7.0", "comment": { "comments": [ { "id": "281357", "author": { "name": "mpmiranda", "key": "mpmiranda", "displayName": "Mauro Parra-Miranda", "active": true, "timeZone": "America/Mexico_City" }, "body": "Hello!\n\nCan you please remove the elements of the window, then null them after closing it? Check this sample:\n\nhttps://gist.github.com/mauropm/5923980\n\n\nRight now, your code says \"I will close this window, but I didn't null it, so I can reopen it if I want\". \n\nBest,\n\nMauro", "updateAuthor": { "name": "mpmiranda", "key": "mpmiranda", "displayName": "Mauro Parra-Miranda", "active": true, "timeZone": "America/Mexico_City" }, "created": "2013-11-26T06:45:17.000+0000", "updated": "2013-11-26T06:45:17.000+0000" }, { "id": "281384", "author": { "name": "philet", "key": "philet", "displayName": "Philippe Wueger", "active": true, "timeZone": "Europe/Berlin" }, "body": "Hi Mauro\n\nThanks for your input. I have changed my test app in the following way:\n\n{code}\n\tbutton.addEventListener('click', function(evt) {\n\t\twin.close();\n\t\twin.remove(listView);\n\t\tlistView = null;\n\t\twin.remove(button);\n\t\tbutton = null;\n\t\twin = null;\n\t});\n{code}\n\nWhen I do this, the leak goes away. However, if I change the event listener to\n{code}\n\t events: {\n\t \t'click': function(evt) {\n\t \t\tvar item = evt.section.getItemAt(evt.itemIndex);\n\t \t\titem.info.text = 'Clicked';\n\t \t\tevt.section.updateItemAt(evt.itemIndex, item);\n\t \t}\n{code}\n\nand I click on the \"info\" label, I have the memory leak again. The memory leak does not occur if I do not click the \"info\" label.\n\nAny further suggestions?\n\n(I will adjust the description in this item to reflect the new test app.)\n\nMany thanks & regards,\nPhilippe\n\n", "updateAuthor": { "name": "philet", "key": "philet", "displayName": "Philippe Wueger", "active": true, "timeZone": "Europe/Berlin" }, "created": "2013-11-26T10:28:47.000+0000", "updated": "2013-11-26T10:28:47.000+0000" }, { "id": "281411", "author": { "name": "mpmiranda", "key": "mpmiranda", "displayName": "Mauro Parra-Miranda", "active": true, "timeZone": "America/Mexico_City" }, "body": "Hello!\n\nIn the second example of click, please remove the event from the label before nulling it. Remember: in order to remove an event, you have not to use anonymous functions, but named ones, so it will be easier. Right now your object is holding in memory because it does have an event listener attached. \n\nBTW, the GC won't usually kick in all time. You need to repeat your testing a lot of times, so the memory reach a memory warning or similar, so that will force the GC to clean the memory. \n\nBest,\n\nMauro", "updateAuthor": { "name": "mpmiranda", "key": "mpmiranda", "displayName": "Mauro Parra-Miranda", "active": true, "timeZone": "America/Mexico_City" }, "created": "2013-11-26T15:07:57.000+0000", "updated": "2013-11-26T15:07:57.000+0000" }, { "id": "281419", "author": { "name": "philet", "key": "philet", "displayName": "Philippe Wueger", "active": true, "timeZone": "Europe/Berlin" }, "body": "Hi Mauro\n\n{quote}\nIn the second example of click, please remove the event from the label before nulling it. \n{quote}\n\nIn my understanding, I do not have direct access to the objects created by a list item, so how can I remove the event listener on the label?\nDo you mean that I should remove the 'click' event listener from the template used in the ListView?\n\nI tried this (see code below), but this did not fix the problem.\n\nAny further ideas?\n\nMany thanks & regards,\nPhilippe\n\n{code}\nvar baseWin = Ti.UI.createWindow({backgroundColor: 'red'});\n\nvar b = Ti.UI.createButton({\n\ttitle: 'Click',\n\tcolor: 'red',\n\tselectedColor: 'blue',\n\theight: '100dp',\n\twidth: '150dp',\n\tbackgroundImage: 'appicon.png',\n\tbackgroundColor: 'green',\n\tstyle: Titanium.UI.iPhone ? Titanium.UI.iPhone.SystemButtonStyle.PLAIN : 0 \n});\n\n\nb.addEventListener('click', function(evt) {\n\n\tvar win = Ti.UI.createWindow({backgroundColor: 'white'});\n\t\n\tvar clickCallback = function(evt) {\n\t\tvar item = evt.section.getItemAt(evt.itemIndex);\n\t\titem.info.text = 'Clicked';\n\t\tevt.section.updateItemAt(evt.itemIndex, item);\t\t\n\t};\n\n\t\n\t// Create a custom template that displays an image on the left, \n\t// then a title next to it with a subtitle below it.\n\tvar myTemplate = {\n\t childTemplates: [\n\t { // Title \n\t type: 'Ti.UI.Label', // Use a label for the title \n\t bindId: 'info', // Maps to a custom info property of the item data\n\t properties: { // Sets the label properties\n\t color: 'black',\n\t font: { fontFamily:'Arial', fontSize: '20dp', fontWeight:'bold' },\n\t left: '60dp', top: 0,\n\t },\n\t events: {\n\t \t'click': clickCallback\n\t }\n\t },\n\t { // Subtitle\n\t type: 'Ti.UI.Label', // Use a label for the subtitle\n\t bindId: 'es_info', // Maps to a custom es_info property of the item data\n\t properties: { // Sets the label properties\n\t color: 'gray',\n\t font: { fontFamily:'Arial', fontSize: '14dp' },\n\t left: '60dp', top: '25dp',\n\t }\n\t }\n\t ]\n\t};\n\t\n\tvar listView = Ti.UI.createListView({\n\t // Maps myTemplate dictionary to 'template' string\n\t templates: { 'template': myTemplate },\n\t // Use 'template', that is, the myTemplate dict created earlier\n\t // for all items as long as the template property is not defined for an item.\n\t defaultItemTemplate: 'template'\n\t});\n\tvar sections = [];\n\t\n\tvar fruitSection = Ti.UI.createListSection({ headerTitle: 'Fruits / Frutas'});\n\tvar fruitDataSet = [\n\t // the text property of info maps to the text property of the title label\n\t // the text property of es_info maps to text property of the subtitle label\n\t // the image property of pic maps to the image property of the image view\n\t { info: {text: 'Apple'}, es_info: {text: 'Manzana'}, pic: {image: 'apple.png'}},\n\t { info: {text: 'Banana'}, es_info: {text: 'Banana'}, pic: {image: 'banana.png'}}\n\t];\n\tfruitSection.setItems(fruitDataSet);\n\tsections.push(fruitSection);\n\t\n\tvar vegSection = Ti.UI.createListSection({ headerTitle: 'Vegetables / Verduras'});\n\tvar vegDataSet = [\n\t { info: {text: 'Carrot'}, es_info: {text: 'Zanahoria'}, pic: {image: 'carrot.png'}},\n\t { info: {text: 'Potato'}, es_info: {text: 'Patata'}, pic: {image: 'potato.png'}}\n\t];\n\tvegSection.setItems(vegDataSet);\n\tsections.push(vegSection);\n\n\tlistView.setSections(sections);\n\t\n\tvar button = Ti.UI.createButton({\n\t\ttitle: 'Close Window',\n\t\tcolor: 'black',\n\t\theight: '100dp',\n\t\twidth: '150dp',\n\t\tbackgroundColor: 'green',\n\t});\n\t\n\tbutton.addEventListener('click', function(evt) {\n win.close();\n myTemplate.childTemplates[0].events.click = null;\n\t\tclickCallback = null;\n win.remove(listView);\n listView = null;\n win.remove(button);\n button = null;\n win = null;\n\t});\t\n\t\n\twin.add(listView);\n\twin.add(button);\n\twin.open();\n\t\n});\n\nbaseWin.add(b);\nbaseWin.open();\n{code}", "updateAuthor": { "name": "philet", "key": "philet", "displayName": "Philippe Wueger", "active": true, "timeZone": "Europe/Berlin" }, "created": "2013-11-26T16:07:00.000+0000", "updated": "2013-11-26T16:07:00.000+0000" }, { "id": "414205", "author": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Closing ticket as duplicate.", "updateAuthor": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-03-21T16:57:11.000+0000", "updated": "2017-03-21T16:57:11.000+0000" } ], "maxResults": 7, "total": 7, "startAt": 0 } } }