Problem
When scrolling a table view, memory rapidly diminishes until you stop scrolling. Once you stop scrolling AND LIFT UP YOUR FINGER the memory releases.
How To Reproduce
* Drop the following in an app.js
* Scroll down and watch the memory fall
* Touch and hold down with your finger to interrupt the table's deceleration and freeze it in place (figuratively speaking)
* Watch the memory stay where it is
* Let go with your finger
* Watch the memory go back up
var PLEASE_BLEED_MEMORY = true;
var win = Ti.UI.createWindow();
var rows = [];
for (var i = 0; i < 2000; i++) {
var row = Ti.UI.createTableViewRow();
row.add(row.label = Ti.UI.createLabel());
if (PLEASE_BLEED_MEMORY) {
row.label.text = 'blood and guts MU HA HA HA';
}
rows.push(row);
}
win.add(Ti.UI.createTableView({ data: rows, bottom: 30 }));
win.add(win.freeMemory = Ti.UI.createLabel({
text: 'Free Memory: Loading...', textAlign: 'center',
bottom: 0, height: 30, color: '#fff', backgroundColor: '#000'
}));
setInterval(function() {
win.freeMemory.text = 'Free Memory: ' + Ti.Platform.availableMemory;
}, 500);
win.open();
Issue is likely: TiUITableviewRowProxy : DeepScanForProxyOfViewContainingPoint(). The issue here is that as part of the fast enumeration, [targetView subviews] is (possibly) called multiple times in a row, on the main thread, which cannot autorelease as long as presses are ongoing. We need some judicious use of autorelease pools in our hit detection (and to make sure we don't call this method more than once per scan anyway).
I have isolated this down to TiViewProxy.m's line 1025 (probably 1025...): [[view retain] performSelector:@selector(autorelease) withObject:nil afterDelay:0.5]; If I comment this out, the memory doesn't rapidly drop.
Introduced by [TIMOB-5326].
D'oh. I should have checked comments earlier. I've arrived to the same agreement, and it's well explained in the performSelector:...:afterDelay:'s docs- "The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode." While the tableview is under finger, the run loop mode is tracking, not default. Thus, it all makes sense. The solution, however, hrm...
While the fix does free up memory earlier, memory still fluctuates way more than it should. Let me quantify that: 1. Use the app.js in the original report 2. Memory began at 45mb 3. Scroll to the bottom; notice the scrolling gets choppier and choppier 4. Memory dropped from 45mb to 30mb 5. Memory quickly jumps up to where it started Now comment out the delayed view autorelease, and perform the same test. 1. Same app.js, only difference is no delayed view autorelease 2. Memory began at 45mb 3. Scroll to the bottom; scrolling does get choppy, but it does not "skip" as much as it did previously 4. Memory dropped from 45mb to 42mb 5. Memory quickly jumps up to where it started What I want to communicate is that the fix that was introduced for one, small scenario in [TIMOB-5326] has a big performance impact on the table view. The app that I am working on is very scroll heavy, and we need as much help as we can get. Our memory usage is already sensitive, and 15 mb swings aren't going to help us stay stable.
Based on experimentation in the customer's app, one way around this could be to add a method to TiViewProxy that doesn't delay the autorelease of the view. In situations where it is safe to do so and we need the performance, we could call this method instead: Such as in TiUITableViewRowProxy.m, line 636:
The method would be the same as the existing detachView, but without the delayed autorelease:
Declare that in the TiViewProxy.h, and we should be good to go. Of course, y'all know the implications of this change way better than I do. I'm only basing this on behavior from one app.
This issue was introduced by the 5326 fix, and so the associated bug should be re-tested and resolved in conjunction with this one.
Closing. Fix verified on iPod running 4.3.3 with SDK 1.8.0.v20111006001414. Free memory stays stable throughout scrolling.
Reopening. Issue is reproducible in build 1.8.0.v20111101161253 with an iPod 4.3.3. Free memory starts at around 70, but as I scrolled down it went to less then 2.
This issue was resolved for a custom branch only and the test case does not apply to the 1.8 release. Should be reclosed as resolved.