Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-4463] listeners property of proxy objects no longer available in 1.7.X (inconsistant compared with 1.6.2)

GitHub Issuen/a
TypeBug
PriorityMedium
StatusClosed
ResolutionInvalid
Resolution Date2011-06-21T12:13:02.000+0000
Affected Version/sRelease 1.7.0
Fix Version/sn/a
ComponentsiOS
Labelsn/a
ReporterRussell Munson
AssigneeReggie Seagraves
Created2011-05-17T07:39:16.000+0000
Updated2017-03-24T18:16:04.000+0000

Description

Accessing a Proxy's listeners property doesn't work as expected in mobilesdk 1.7.0 (as it did in 1.6.2). Given the proxy "myView" (any view type): * 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. * 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. This code demonstrates the behavior:
var win = Ti.UI.createWindow({
	backgroundColor : "blue",
	exitOnClose:true,
	layout:'vertical'
});

var myView=Ti.UI.createView({
	backgroundColor:'green',
	height:200,
	width:200
});

win.add(myView);
win.open();

myView.addEventListener('click', function(){
	Ti.API.info('Click event fired');
});

Ti.API.info(myView.listeners.click);
Ti.API.info("myView.listeners.click: " + myView.listeners.click); // comment this for android to avoid exception
The results are as follows: Titanium 1.6 on iOS:
[INFO] myView.listeners: [object Object]

[INFO] myView.listeners.click: function () {

	Ti.API.info('Click event fired');

}
Titanium 1.8, 1.7, 1.7.1 on iOS:
[INFO] myView.listeners: [object Object]

[INFO] myView.listeners.click: 1
See a full justification for this functionality [in this comment|#comment-156716]. Note that this functionality has never been available for Android, as [this comment|#comment-157207] demonstrates.

Comments

  1. Russell Munson 2011-05-27

    Any feedback on this? I 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.
  2. Paul Dowsett 2011-05-28

    Russell In 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). Please read the [Submitting Bug Reports](http://wiki.appcelerator.org/display/guides/Contributing+to+Titanium#ContributingtoTitanium-SubmittingBugReports) guide before raising tickets. Thank you
  3. Russell Munson 2011-05-31

    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. I'm adding a more involved example as well - will update shortly.
  4. Russell Munson 2011-05-31

    Thanks for the feedback Paul, I've added a second example, hopefully it illustrates the issue more clearly.
  5. Russell Munson 2011-06-07

    Was this additional example more clear?
  6. Russell Munson 2011-06-09

    The commit that included the change to the addEventListener method for TiProxy is viewable at: https://github.com/appcelerator/titanium_mobile/commit/a34873938a950f3def3007f5a9f8eb8935867077
  7. Paul Dowsett 2011-06-10

    Russell I 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. It'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. I hope you can appreciate how beneficial this is in the long term. Please read [Creating Good Use-cases](http://wiki.appcelerator.org/display/guides/Contributing+to+Titanium#ContributingtoTitanium-CreatingGoodUsecases), which explains exactly what is required. Thanks for your understanding.
  8. Russell Munson 2011-06-10

    No need for apologies at all. I don't mean to waste anyones time. I've read through the wiki doc you provided, but it seems I am just not understanding what is deficient with the example. If you paste the code as provided into a blank app.js file, it will execute perfectly. In 1.7.0 = you will get an alert with the words "Listener executed". In 1.6.3 - you will not, since it has been successfully removed. More details will be logged to console. The 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...
       var myView=Ti.UI.createView();
       myView.addEventListener('eventName', function(){/* eventListener*/});
       alert(myView.listeners.eventName);
       
    In 1.7.0 you will get an alert with the text: 1 In 1.6.2 you will get an alert with the text: ( "" ) I've attached screenshots of the updated Example 1 code running in both sdk versions.
  9. Paul Dowsett 2011-06-13

    Russell Where 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. Would you please explain what you use it for and why you need it? This will help justify the need to get it resolved. I have tried the following code to test this in my environment:
       var win = Ti.UI.createWindow({
       	backgroundColor : "blue",
       	exitOnClose:true,
       	layout:'vertical'
       });
       
       var myView=Ti.UI.createView({
       	backgroundColor:'green',
       	height:200,
       	width:200
       });
       
       win.add(myView);
       win.open();
       
       myView.addEventListener('click', function(){
       	Ti.API.info('Click event fired');
       });
       
       Ti.API.info(myView.listeners.click);
       Ti.API.info("myView.listeners.click: " + myView.listeners.click); // comment this for android to avoid exception
       
    The results are as follows: Titanium 1.8, 1.7, 1.7.1 on iOS:
       [INFO] myView.listeners: [object Object]
       
       [INFO] myView.listeners.click: 1
       
    Titanium 1.6 on iOS:
       [INFO] myView.listeners: [object Object]
       
       [INFO] myView.listeners.click: function () {
       
       	Ti.API.info('Click event fired');
       
       }
       
    Does this work for you?
  10. Russell Munson 2011-06-14

    Hi Paul, Thanks again. Yes, that example illustrates issue well when inspecting the console messages. The main use case, is the allow a developer to remove an eventListener or set of event listeners in the case that : * The event handler is an anonymous function * 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) * The named function used as the eventHandler is not known in the current scope (adding, or removing an event listener in a utility function) Use 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. Since 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.
  11. Paul Dowsett 2011-06-20

    To compare the iOS behavior with Android, I have tried my code above on Android using the following SDKs: * Titanium 1.5.2 (2011/01/27 09:02 912149...) * Titanium 1.6.1 (2011/03/15 11:45 fdc0c5) * Titanium 1.6.2 (2011/04/18 17:16 78906d) * Titanium 1.7.1 (2011/06/17 00:13 293a6d...) All give the same result:
        (kroll$1) [1054,1170] myView.listeners: undefined
        
    I will open a feature request for the android project after I move this ticket to ios.
  12. Blain Hamon 2011-06-21

    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). The 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.
  13. Lee Morris 2017-03-24

    Closing ticket as invalid with reference to the above comments.

JSON Source