can someone please clarify this: when a table cell scrolls off the screen, does it still reside in memory? The reason why I am asking this is, I had to use a tableview that has to handle too many cells.
when you scroll the table up, it loads next set of visible cells. At this point, i want to make sure , that all the cells thats been scrolled off, wont be added as a heap slowing the performance. Thanks,
If you queue the cells then each cell, as soon as it disappears from the screen (that is, it is scrolled up or down), is re-used for other new cells entering in the screen.
This means that if your screen can show no more than 7 cells at the moment, the memory taken for cell allocations will not be higher than the one needed for exactly 7 of them.
The advantage of this approach is memory saving but also performance improvement as you don't need to alloc/init the cells each time.
Of course this is valid if you appropriately autorelease cells when created, if you use the same queue identifier and of course it is independent on your way to manage the data you will insert in the queue (images, strings, ...)
Related
Could anyone tell me the tradeoff in performance/memory usage between using static and dynamic cells in a UITableView?
Here's my situation: I have a TableView with 6 different sections. The first section is the only section in my tableView that holds a different number of cells each time the view loads, depending on the current state of the app. i.e. I have declared 12 static cells for that section in interface builder, however I only display a certain number of those cells depending on the user's interaction with the app thus far. The other 5 tableView sections all contain UISwitches and textFields that never change.
So say I statically allocated 50 cells for that first section, but still only displayed maybe just half of them depending on the state of the app. I would want to be able to display up to 50 cells though. How would this affect the speed or performance of my app? Would doing the entire tableView dynamically and redrawing the switches and textFields for the other sections each time lead to a better application performance?
UITableView itself is only tangentially related to performance in this situation. The real issue is how and when you allocate new cells.
If you have static cells whose contents never change, and you create them using the interface builder (née Interface Builder), you will see that allocation happening only once, usually in -viewDidLoad, and for the lifetime of that table (or at least until -viewDidUnload) these cells will exist and not need to be reallocated.
But this is a trade-off. Now your cells will load faster, but your app will have more memory. You'll just have to decide on a case-by-case basis whether this is slowing down your app dramatically, in which case you may want to lazily load your static cells the typical "dynamic" style in your data source cell-fetching method.
In my iPad application, I've made a segmented control and one segment of which -when clicked- displays a long list (about 300) with images and labels from the local SQLite database. This is taking a lot of time to load and puts the app activity to halt while it's loading all of it from the database.
Although I've applied an activity indicator for the time being, but that looks very shoddy. Can anyone tell me how to apply Lazy Loading in a way that When the button is clicked to open that view, instead of loading all the content at once, it fetches only the content that's displayed on the content initially (about 9 images with lablels).
Thanks in advance.
You should implement paging on the list. Load first 25 item and then add button ("Next 25") on tableFooterView, which will load another 25.
If you use a UITableView, you might have a better chance.
A UITableViewCell loads cells one at a time(gives you the index of the item it is trying to load), so if you use a table view, it will only load the number of items that it needs to display. You tell it how many items there are, how tall they are, etc.
It also reuses cells, so it gives a lot better performance than creating 300 different views in memory at a time.
A UIScrollView doesn't know about your "items", so it lets you push as many items as you want into a view, and then adds a scroll bar. No optimization here for memory usage, or database access.
I have a UITableView. each row is heavy object with videos, images etc.
When user scrolls this view how can I release the memory of not visible rows and load the current visible rows?
I assume you're talking about releasing memory that's used by the images and videos of your row, and not the row itself.
In your tableview delegate,
-(void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView
tells you when the tableview scrolling has stopped.
[myTableView indexPathsForVisibleRows]
gives you an array of what is visible.
If your row is not in this array, it is not visible, and you can do your image/video cleanup on it.
Are you recycling UITableViewCells as per Apple's recommendations? If not, you MUST read Apple's docs. Inside the cellForRowAtIndexPath: delegate you should have something [customCell setMediaObjects:]. Inside your customCell class you can release all the previous mediaObjects from memory.
As others have said, you should make sure you are recycling cells properly, and not destroying things you would need to recreate anyway when the cell is reused.
But, you may want to release other assets that the cell or its views are retaining. Or if that cell has any pending download requests, for example, you may want to reset their priority or even cancel them when the cell is offscreen.
I think the cleanest way to do this is to just override -[UITableViewCell prepareForReuse]
This is called when the cell is put back into the reuse queue. If the user is moving up and down the table quickly, you may not want to clean the cell up the moment the cell is off the screen (by looking at indexPathsForVisibleRows, for example).
But when the cell is actually put back in the reuse queue, that is a good time to do that work since you know that cell won't appear again on screen until you dequeue and configure it again.
A Closer Look at Table-View Cells - Apple Documentation
When you call dequeueReusableCellWithIdentifier first 10 (just ten cells can be shown at screen at one moment) cells will be created, then they will be just loaded and configured for showing.
So, cells are not released.
I have a custom cell that contains a button in a table view. The button is used as a toggle to essentially serve as a "checkbox" for a user to check off certain items in the list. I was having the issue in which the buttons in these table cells seemed to be sharing memory locations as a result of the dequeuereusablecellwithidentifier. When a button was pressed, it would also press every 4th or 5th button in the list.
I changed it to create my cells in a method into an array which then populates the tableview. This works fine for what I am trying to achieve, however it poses an issue when dealing with large row counts. The tableview itself runs quickly, but the initial load can be 3-4 seconds at times when there are over 100 rows. The iteration to create the cells and then populate it to the tableview is quite cumbersome.
What other methods can you populate a tableview with custom cells and buttons while still retaining unique memory for the buttons within?
Any help would be appreciated!
Thanks :)
You definitely don't want to change the way the creation of cells work- dequeuereusablecellwithidentifier is a very good thing for the reasons your seeing.
The solution is that you should store the result of the button/checkbox press in a separate data structure, like an NSArray full of NSNumber. As your table scrolls and cells are reused, you reset the state of the checkbox to whatever state it should be based on your NSArray.
Good luck!
I'm curious just how expensive in as far as resources go is UITableView's reloadData? I have an app which will make roughly 10 subsequent HTTP requests, and as it gets data / preps, it reloads the tableView. As the data set grows larger and larger, it's becoming very sluggish. I'm trying to figure out if it's because of the amount of times I'm reloading the tableView or because of how I'm grabbing/parsing the data.
What's the best practice in this case?
From UITableView.h:
- (void)reloadData; // reloads everything from scratch. redisplays visible rows. because we only keep info about visible rows, this is cheap. will adjust offset if table shrinks
"This is cheap."
implement your table view methods well and it'll be no big deal to call this function all the time.
On a side note, you should try to use the appropriate methods to animate adding and removing rows if you are thinking of using reloadData for that.
The best practice is to have your implementation of cellForRowAtIndexPath: do as little work as possible. In fact, it really shouldn't be doing any work except populating the UITableViewCell instance with the data it needs to display.
You should be using cached UITableViewCells so you don't have to allocate a new cell each time. If you can do your parsing and such in a separate thread and make the parsed data, ready to present, accessible to cellForRowAtIndexPath:, you shouldn't have any performance problems.
You didn't say if you were using a custom UITableViewCell subclass, but if you are, deep view hierarchies can also present a performance problem, since each view in the hierarchy gets drawn. The flatter you can make UITableViewCells, the better.
Hope that gets you moving in the right direction.
Best thing to do is profile your app to see where it is slow.
That said, if your table cells are all the same height, then I think
reloadData
only has to call
cellForRowAtIndexPath
for cells that are visible on screen.
Table view reload expense is:
Figuring out how many sections and
rows per sections you have
getting row heights.
Row heights in particular are figured out for all elements of the table, anytime you call reload data.
The remaining expense is cellForRowAtIndexPath, which is usually not too bad because it only is called for as many rows as are on the screen. It can be bad when scrolling if you do not reuse cells like you are supposed to.
The key for you is probably, to ask yourself what triggers the HTML load and possibly move that into a background thread.
Boot To The Head is correct.
I'm doing a progressive one-by-one article list update in Instapaper, and I call -reloadData on each completed download. Sounds similar to what you're doing. It doesn't result in any noticeable performance slowdowns.