Titanium Mobile
  1. Titanium Mobile
  2. TIMOB-4463

listeners property of proxy objects no longer available in 1.7.X (inconsistant compared with 1.6.2)

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Medium Medium
    • Resolution: Invalid
    • Affects Version/s: Release 1.7.0
    • Fix Version/s: None
    • Component/s: iOS
    • Labels:
      None
    • Environment:

      Titanium 1.6.2 and 1.7.0

      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.

      Note that this functionality has never been available for Android, as this comment demonstrates.

        Activity

        Hide
        Russell Munson added a comment - - edited

        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:
        (
        "<KrollCallback: 0x#######>"
        )

        I've attached screenshots of the updated Example 1 code running in both sdk versions.

        Show
        Russell Munson added a comment - - edited 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: ( "<KrollCallback: 0x#######>" ) I've attached screenshots of the updated Example 1 code running in both sdk versions.
        Hide
        Paul Dowsett added a comment - - edited

        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?

        Show
        Paul Dowsett added a comment - - edited 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?
        Hide
        Russell Munson added a comment - - edited

        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.

        Show
        Russell Munson added a comment - - edited 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.
        Hide
        Paul Dowsett added a comment -

        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.

        Show
        Paul Dowsett added a comment - 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.
        Hide
        Blain Hamon (Inactive) added a comment -

        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.

        Show
        Blain Hamon (Inactive) added a comment - 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.

          People

          • Assignee:
            Reggie Seagraves
            Reporter:
            Russell Munson
          • Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development