{ "id": "65411", "key": "TIMOB-4463", "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": "7", "description": "", "name": "Invalid" }, "resolutiondate": "2011-06-21T12:13:02.000+0000", "created": "2011-05-17T07:39:16.000+0000", "priority": { "name": "Medium", "id": "3" }, "labels": [], "versions": [ { "id": "11244", "name": "Release 1.7.0", "archived": true, "released": true, "releaseDate": "2011-06-13" } ], "issuelinks": [], "assignee": { "name": "rseagraves", "key": "rseagraves", "displayName": "Reggie Seagraves", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2017-03-24T18:16:04.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": "Accessing a Proxy's listeners property doesn't work as expected in mobilesdk 1.7.0 (as it did in 1.6.2).\r\n\r\nGiven the proxy \"myView\" (any view type):\r\n\r\n* In 1.6.2 - myView.listeners would give you an object with key/value pairs equating to the listener type/array of callback functions bound as Listeners to the view.\r\n* In 1.7.0 - myView.listeners gives you an object, however the value is now an integer, which seems to be equal to the length of what the callback array would be.\r\n\r\nThis code demonstrates the behavior:\r\n\r\n{code:lang=javascript}\r\nvar win = Ti.UI.createWindow({\r\n\tbackgroundColor : \"blue\",\r\n\texitOnClose:true,\r\n\tlayout:'vertical'\r\n});\r\n\r\nvar myView=Ti.UI.createView({\r\n\tbackgroundColor:'green',\r\n\theight:200,\r\n\twidth:200\r\n});\r\n\r\nwin.add(myView);\r\nwin.open();\r\n\r\nmyView.addEventListener('click', function(){\r\n\tTi.API.info('Click event fired');\r\n});\r\n\r\nTi.API.info(myView.listeners.click);\r\nTi.API.info(\"myView.listeners.click: \" + myView.listeners.click); // comment this for android to avoid exception\r\n{code}\r\n\r\nThe results are as follows:\r\n\r\nTitanium 1.6 on iOS:\r\n\r\n{code}\r\n[INFO] myView.listeners: [object Object]\r\n\r\n[INFO] myView.listeners.click: function () {\r\n\r\n\tTi.API.info('Click event fired');\r\n\r\n}\r\n{code}\r\n\r\nTitanium 1.8, 1.7, 1.7.1 on iOS:\r\n\r\n{code}\r\n[INFO] myView.listeners: [object Object]\r\n\r\n[INFO] myView.listeners.click: 1\r\n{code}\r\n\r\nSee a full justification for this functionality [in this comment|#comment-156716].\r\n\r\nNote that this functionality has never been available for Android, as [this comment|#comment-157207] demonstrates.", "attachment": [], "flagged": false, "summary": "listeners property of proxy objects no longer available in 1.7.X (inconsistant compared with 1.6.2)", "creator": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "subtasks": [], "reporter": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "environment": "Titanium 1.6.2 and 1.7.0", "comment": { "comments": [ { "id": "134382", "author": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "body": "Any feedback on this? \n\nI know it's not part of the public API, but it is something that is sorely missing. Having access to the bound listeners is a extremely useful feature especially when using anonymous functions as listeners.", "updateAuthor": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "created": "2011-05-27T10:14:02.000+0000", "updated": "2011-05-27T10:14:02.000+0000" }, { "id": "134418", "author": { "name": "pdowsett", "key": "pdowsett", "displayName": "Paul Dowsett", "active": true, "timeZone": "Europe/London" }, "body": "Russell\n\nIn order for us to progress this issue, edit your ticket to include a proper [Use-case|http://wiki.appcelerator.org/display/guides/Contributing+to+Titanium#ContributingtoTitanium-CreatingGoodUsecases].\n\nPlease read the [Submitting Bug Reports|http://wiki.appcelerator.org/display/guides/Contributing+to+Titanium#ContributingtoTitanium-SubmittingBugReports] guide before raising tickets.\n\nThank you", "updateAuthor": { "name": "pdowsett", "key": "pdowsett", "displayName": "Paul Dowsett", "active": true, "timeZone": "Europe/London" }, "created": "2011-05-28T02:42:10.000+0000", "updated": "2011-05-28T02:42:10.000+0000" }, { "id": "134481", "author": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "body": "Hi Paul, the example code I have in the ticket demonstrates the issue, with output from v 1.6.2 and 1.7.0-rc1.\n\nI'm adding a more involved example as well - will update shortly.", "updateAuthor": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "created": "2011-05-31T07:47:16.000+0000", "updated": "2011-05-31T07:47:16.000+0000" }, { "id": "134490", "author": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "body": "Thanks for the feedback Paul, I've added a second example, hopefully it illustrates the issue more clearly.", "updateAuthor": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "created": "2011-05-31T08:18:25.000+0000", "updated": "2011-05-31T08:26:37.000+0000" }, { "id": "147073", "author": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "body": "Was this additional example more clear? ", "updateAuthor": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "created": "2011-06-07T13:27:51.000+0000", "updated": "2011-06-07T13:27:51.000+0000" }, { "id": "156369", "author": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "body": "The commit that included the change to the addEventListener method for TiProxy is viewable at: \r\n\r\nhttps://github.com/appcelerator/titanium_mobile/commit/a34873938a950f3def3007f5a9f8eb8935867077\r\n", "updateAuthor": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "created": "2011-06-09T16:04:12.000+0000", "updated": "2011-06-09T16:04:12.000+0000" }, { "id": "156425", "author": { "name": "pdowsett", "key": "pdowsett", "displayName": "Paul Dowsett", "active": true, "timeZone": "Europe/London" }, "body": "Russell\r\n\r\nI am very sorry, but the code you have provided is not a usecase. A usecase is a concise piece of code, without any unnecessary functions, views and properties, that runs without any modification when pasted into a blank app.js file. Note that it will almost always contain a createWindow() method.\r\n\r\nIt's important that we get this right before the core devs see it, as otherwise they will waste a lot of time replicating the issue rather than fixing it.\r\n\r\nI hope you can appreciate how beneficial this is in the long term.\r\n\r\nPlease read [Creating Good Use-cases|http://wiki.appcelerator.org/display/guides/Contributing+to+Titanium#ContributingtoTitanium-CreatingGoodUsecases], which explains exactly what is required.\r\n\r\nThanks for your understanding.", "updateAuthor": { "name": "pdowsett", "key": "pdowsett", "displayName": "Paul Dowsett", "active": true, "timeZone": "Europe/London" }, "created": "2011-06-10T04:54:05.000+0000", "updated": "2011-06-10T04:54:05.000+0000" }, { "id": "156446", "author": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "body": "No need for apologies at all. I don't mean to waste anyones time.\r\nI've read through the wiki doc you provided, but it seems I am just not understanding what is deficient with the example.\r\n\r\nIf you paste the code as provided into a blank app.js file, it will execute perfectly.\r\n\r\nIn 1.7.0 = you will get an alert with the words \"Listener executed\".\r\nIn 1.6.3 - you will not, since it has been successfully removed.\r\n\r\nMore details will be logged to console. \r\n\r\nThe only other thing I can think of is to go back to the first example I posted since it is more simple... and add an alert...\r\n\r\n{code}\r\nvar myView=Ti.UI.createView();\r\nmyView.addEventListener('eventName', function(){/* eventListener*/});\r\nalert(myView.listeners.eventName);\r\n{code}\r\n\r\nIn 1.7.0 you will get an alert with the text: 1\r\nIn 1.6.2 you will get an alert with the text: \r\n (\r\n\"\"\r\n )\r\n\r\nI've attached screenshots of the updated Example 1 code running in both sdk versions.", "updateAuthor": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "created": "2011-06-10T09:24:58.000+0000", "updated": "2011-06-10T09:27:13.000+0000" }, { "id": "156560", "author": { "name": "pdowsett", "key": "pdowsett", "displayName": "Paul Dowsett", "active": true, "timeZone": "Europe/London" }, "body": "Russell\r\n\r\nWhere have you seen this documented? I don't think it has ever been available for Android, so I am wondering whether it has been removed from iOS to make the platforms consistent, or there is a justification for it to be added to Android.\r\n\r\nWould you please explain what you use it for and why you need it? This will help justify the need to get it resolved.\r\n\r\nI have tried the following code to test this in my environment:\r\n\r\n{code:lang=javascript}\r\nvar win = Ti.UI.createWindow({\r\n\tbackgroundColor : \"blue\",\r\n\texitOnClose:true,\r\n\tlayout:'vertical'\r\n});\r\n\r\nvar myView=Ti.UI.createView({\r\n\tbackgroundColor:'green',\r\n\theight:200,\r\n\twidth:200\r\n});\r\n\r\nwin.add(myView);\r\nwin.open();\r\n\r\nmyView.addEventListener('click', function(){\r\n\tTi.API.info('Click event fired');\r\n});\r\n\r\nTi.API.info(myView.listeners.click);\r\nTi.API.info(\"myView.listeners.click: \" + myView.listeners.click); // comment this for android to avoid exception\r\n{code}\r\n\r\nThe results are as follows:\r\n\r\nTitanium 1.8, 1.7, 1.7.1 on iOS:\r\n\r\n{code}\r\n[INFO] myView.listeners: [object Object]\r\n\r\n[INFO] myView.listeners.click: 1\r\n{code}\r\n\r\nTitanium 1.6 on iOS:\r\n\r\n{code}\r\n[INFO] myView.listeners: [object Object]\r\n\r\n[INFO] myView.listeners.click: function () {\r\n\r\n\tTi.API.info('Click event fired');\r\n\r\n}\r\n{code}\r\n\r\n\r\nDoes this work for you?", "updateAuthor": { "name": "pdowsett", "key": "pdowsett", "displayName": "Paul Dowsett", "active": true, "timeZone": "Europe/London" }, "created": "2011-06-13T01:41:35.000+0000", "updated": "2011-06-20T14:20:53.000+0000" }, { "id": "156716", "author": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "body": "Hi Paul,\r\n\r\nThanks again. Yes, that example illustrates issue well when inspecting the console messages.\r\n\r\nThe main use case, is the allow a developer to remove an eventListener or set of event listeners in the case that : \r\n* The event handler is an anonymous function\r\n* You wish to remove all event listeners of a specific type - 'click' in the example above. (see Ex2's removeAllListeners function in the bug description for an example)\r\n* The named function used as the eventHandler is not known in the current scope (adding, or removing an event listener in a utility function)\r\n\r\nUse of anonymous functions, that depend on closures to keep locally scoped variables in scope, are a big part of javascript development. The role functions as first-class objects in javascript is one if it's greatest strengths. \r\n\r\nSince removeEventListener requires that you pass a function as the second parameter, there is no way (other than accessing the .listeners property) to remove a bound anonymous handler.", "updateAuthor": { "name": "rmunson", "key": "rmunson", "displayName": "Russell Munson", "active": true, "timeZone": "America/New_York" }, "created": "2011-06-14T09:04:53.000+0000", "updated": "2011-06-14T09:06:15.000+0000" }, { "id": "157207", "author": { "name": "pdowsett", "key": "pdowsett", "displayName": "Paul Dowsett", "active": true, "timeZone": "Europe/London" }, "body": "To compare the iOS behavior with Android, I have tried my code above on Android using the following SDKs:\r\n\r\n* Titanium 1.5.2 (2011/01/27 09:02 912149...)\r\n* Titanium 1.6.1 (2011/03/15 11:45 fdc0c5)\r\n* Titanium 1.6.2 (2011/04/18 17:16 78906d)\r\n* Titanium 1.7.1 (2011/06/17 00:13 293a6d...)\r\n\r\nAll give the same result:\r\n\r\n{code}\r\n(kroll$1) [1054,1170] myView.listeners: undefined\r\n{code}\r\n\r\nI will open a feature request for the android project after I move this ticket to ios.", "updateAuthor": { "name": "pdowsett", "key": "pdowsett", "displayName": "Paul Dowsett", "active": true, "timeZone": "Europe/London" }, "created": "2011-06-20T13:54:49.000+0000", "updated": "2011-06-20T13:54:49.000+0000" }, { "id": "157305", "author": { "name": "blainhamon", "key": "blainhamon", "displayName": "Blain Hamon", "active": true, "timeZone": "America/Los_Angeles" }, "body": "listeners is a private structure that was never intended for public use. The fact that one can access this from JS is a bug, especially since doing so isn't threadsafe (read: accessing private structures mean there's no safeties, and make it possible to crash Titanium doing so).\n\nThe reason that listeners changed from 1.6 to 1.7 was that it was found to be creating retain cycles (Read: taking up memory when it shouldn't) and to stop this, callbacks *must* be stored on the JS objects, not on the Objective-C proxies.", "updateAuthor": { "name": "blainhamon", "key": "blainhamon", "displayName": "Blain Hamon", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2011-06-21T12:13:02.000+0000", "updated": "2011-06-21T12:13:02.000+0000" }, { "id": "415652", "author": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Closing ticket as invalid with reference to the above comments.", "updateAuthor": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-03-24T18:16:04.000+0000", "updated": "2017-03-24T18:16:04.000+0000" } ], "maxResults": 13, "total": 13, "startAt": 0 } } }