- Application type: mobile
- Titanium SDK: 3.3.0.RC
- Platform & version: iOS 7.1
- Device: iOS simulator, iPhone 5S
- Host Operating System: OSX 10.9.3
I'm using Alloy model-view binding to keep data in a table updated. The app is lazy-loading additional rows and adding it to the collection, triggering Alloy to add new rows to the table. I've created a sample app below to demonstrate the behavior, which allows adding 100 rows to a table view, in chunks of 10. In the sample app, click "Add More Books" to add 10 new books.
I'm experiencing two unexpected behaviors, which are most likely related.
1: Memory Spikes In order to add 100 rows to the table (in chunks of 10), the app created 5400 temporary Titanium objects. They get removed from memory, but cause a memory and processing spike during creation. See Screenshot. Note that the example app only has 1 label per row. My actual app has several views per row, including image views.
2: Data in row controller gets processed for every collection "add" event When adding models to a collection, each "add" event (when a model is added to a collection) executes the controller code for all existing rows again. For example: I have 90 models in a collection and add 10 models. The 10 models will execute the controller code for all 100 rows 10 times. See the debug code in the example app. This prevents me from putting any processor-intensive code into the row controller, such as caching an image.
I'd strongly appreciate if anyone could let me know what I'm doing wrong here, as I'd like to use Alloy's model-view controller binding.
Here's the test app: http://cl.ly/3e142V3B0w0L, or see relevant files below.
views/index.xml:
<Alloy> <Collection src="books" /> <TabGroup id="tabgroup"> <Tab title="Books" icon="KS_nav_views.png"> <Window title="Books"> <RightNavButton platform="ios"> <Button title="Add More Books" id="addMore" onClick="addMoreBooks" /> </RightNavButton> <TableView id="tableView" dataCollection="books"> <Require type="view" src="row" /> </TableView> </Window> </Tab> </TabGroup> </Alloy>views/row.xml:
<Alloy> <TableViewRow height="40"> <View id="outerView"> <Label color="black" text="{title}" /> </View> </TableViewRow> </Alloy>models/books.js
exports.definition = { config: { columns: { "id": "INTEGER PRIMARY KEY", "title": "TEXT" }, adapter: { type: "sql", collection_name: "books", idAttribute: 'id' } }, extendModel: function(Model) { _.extend(Model.prototype, { // extended functions and properties go here }); return Model; }, extendCollection: function(Collection) { _.extend(Collection.prototype, { // extended functions and properties go here }); return Collection; } };controller/index.js:
// Generate 100 models once. var populated = Ti.App.Properties.getBool('populated', false); if (populated === false) { for (var i = 0; i <= 100; i++) { Ti.API.error('Generate Book ' + i); var model = Alloy.createModel('books', { id: i, title: String('Book ' + i) }); model.save(); } Ti.App.Properties.setBool('populated', true); } var chunkSize = 10; var collection = Alloy.Collections.books; collection.comparator = function(model) { return model.get('id'); }; function addMoreBooks() { var lengthBefore = collection.models.length || 0; collection.fetch({ add: true, query: { statement: 'SELECT * from books ORDER BY id ASC LIMIT ? OFFSET ?', params: [chunkSize, lengthBefore] }, success: function(collection) { $.tableView.scrollToIndex(collection.models.length - 1); Ti.API.error('Number of books in view: ' + collection.models.length); } }); } $.tabgroup.open();controllers/row.js
var args = arguments[0] || {}; Ti.API.error('Processing row for : ' + $model.get('title'));