Objects passed from global context to subcontext and then back to global context lose identity
I've found that objects passed to a sub-context and then passed back to the parent context are not the same as the original object in the parent context. Has anybody else experienced this?
I am using a TableView to display a list of cars implemented in a CarList.js file. I am using another file, CarInfo.js, to display information about the car when a row is clicked.
Here's the gist of my code:
##CarList.js
// populate rows for the table view from the carList model.
var carRows = [];
carList.each(function(car) {
carRows.push({ title : car.name, hasChild : true, car : car });
});
var carsTableView = Titanium.UI.createTableView({
data : carRows
});
carsTableView.addEventListener('click', function(e) {
var carInfoWindow = Titanium.UI.createWindow({ url : 'CarInfo.js' });
// pass along the car that the view should be rendered for and
// a callback for saving the car to the data layer.
carInfoWindow.car = e.rowData.car;
carInfoWindow.saveCar = function(carFromSubContext) {
for ( var i = 0; i < carList.length; ++i ) {
if (carList[i] === carFromSubContext) {
// NEVER HAPPENS
alert( 'Car passed from sub-context exists in parent context!' );
}
}
}
Titanium.UI.currentTab.open(carInfoWindow,{animated:true});
});
##CarInfo.js
window = Titanium.UI.currentWindow;
...
saveButton.addEventListener('click', function() {
// update attributes of the car from the view
window.car.name = nameField.value;
...
// save it
window.saveCar(car);
});
The alert() call in the saveCar() callback on the carInfoWindow is never hit. I've inspected the attributes of the cars in carList and carFromSubContext, and there is a car object in carList that has the exact same attributes as carFromSubContext, but it does not pass the identity test in saveCar().
What gives?
5 Answers
-
Accepted Answer
@David - Regarding:
"Cross window messaging operates on the same principles as the fire event"Yeah, I was beginning to believe that. Seems likely since windows run in their own threads and if things weren't cloned you'd have to worry about synchronized access to the data. Still though, references to objects and functions passed down from the parent to the sub context are not null as the Passing Data section of the doc on Window discusses and as my example shows.
To add to the confusion, in my example, the saveCar() callback lives in the parent context and is called by the sub context but accesses data in the parent context (i.e. the carList array). How is the carList variable accessible?
If objects are cloned when passed to sub contexts then objects referenced internally by saveCar() (i.e. carList) would be missed as they are not known about yet. Perhaps the JS-internal objects comprising the scope chain are cloned during this step as well? Or perhaps, the scope chain is not cloned, but when looking up variable references through the scope chain there is something that identifies that the variable lives in a different context and when this happens it grabs a mutex before accessing the data?
So, I'm not really sure what to believe at this point. Perhaps cross window messaging is tasked to a single thread that grabs per-context locks? I don't know…
Lacking an authoritative answer (something I was hoping to get from asking the question), the only remaining alternative is to splunk through the source code; something I don't have time to do. This issue and some others (see below) have resulted in me completely avoiding the use of sub contexts altogether. It would be nice to see examples of how sub contexts can be used in an MVC structure containing an app-wide data layer shared across many contexts… I can't wait for that now.
Other issues with sub contexts:
-
This is documented in the last bullet point on events here https://developer.appcelerator.com/apidoc/mobile/1.2/Titanium.UI.Window-object
The solution to the problem is also outlined with sending events to a window from the global context and vice versa.
-
As far as I know, objects are serialized for events. Perhaps you could add an id field and check that instead.
-
@David - The last bullet point talks about sending events through fireEvent(). I am not sending events. See my code in the question.
@Damien - Yeah, this is what I do. Then I have to copy over all the attributes from the object from the sub-context to the origin object in the parent context. I was hoping I wouldn't have to do this.
-
Cross window messaging operates on the same principles as the fire event I believe and as such:
"You can only send JSON-serializable data in a fireEvent. If you attempt to send objects that have function references, they will be null." applies.Each window runs in it's own thread and objects are cloned (json serialised) - rather than being a reference.